@camstack/addon-pipeline 1.2.628 → 1.2.630

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 (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +35 -0
  3. package/dist/audio-analyzer/index.js +3 -3
  4. package/dist/audio-analyzer/index.mjs +3 -3
  5. package/dist/{custom-model-catalog-D2oFV46F.js → custom-model-catalog-CsHQljey.js} +2 -2
  6. package/dist/{custom-model-catalog-Bu1Lc-iF.mjs → custom-model-catalog-qwNT0qE-.mjs} +2 -2
  7. package/dist/detection-pipeline/index.js +4 -4
  8. package/dist/detection-pipeline/index.mjs +4 -4
  9. package/dist/{dist-DNW4WfVK.mjs → dist-C56NTN3j.mjs} +1061 -24
  10. package/dist/{dist-BjySFF52.js → dist-M09KjuSx.js} +1061 -24
  11. package/dist/{model-catalogs-CwuCl3IY.js → model-catalogs-BmZHxpaD.js} +1 -1
  12. package/dist/{model-catalogs-CCHnV0wA.mjs → model-catalogs-vPj_kgtX.mjs} +1 -1
  13. package/dist/motion-wasm/index.js +1 -1
  14. package/dist/motion-wasm/index.mjs +1 -1
  15. package/dist/{node-CLDkYxRi.js → node-C0CzsJ0N.js} +1 -1
  16. package/dist/{node-CiT7lJCO.mjs → node-CdsOgsyN.mjs} +1 -1
  17. package/dist/pipeline-runner/index.js +3 -3
  18. package/dist/pipeline-runner/index.mjs +3 -3
  19. package/dist/{process-memory-D5WNQP42.js → process-memory-BURe-pwd.js} +1 -1
  20. package/dist/{process-memory-6dtW_uwK.mjs → process-memory-DAhr_IbO.mjs} +1 -1
  21. package/dist/recorder/index.js +2 -2
  22. package/dist/recorder/index.mjs +2 -2
  23. package/dist/{segment-demux-js-DDI4hgPB.js → segment-demux-js-B391lmQZ.js} +1 -1
  24. package/dist/{segment-demux-js-CL6CBu1v.mjs → segment-demux-js-CobIINvd.mjs} +1 -1
  25. package/dist/stream-broker/_stub.js +2 -2
  26. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DrEPZZ6A.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Bxan5418.mjs} +2 -2
  27. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-inxALjZf.mjs +26 -0
  28. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-wSzIkKI-.mjs +26 -0
  29. package/dist/stream-broker/demux-worker-child.js +1 -1
  30. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  31. package/dist/stream-broker/{hostInit-CsSJvo8w.mjs → hostInit-CmFod5rw.mjs} +2 -2
  32. package/dist/stream-broker/index.js +3 -3
  33. package/dist/stream-broker/index.mjs +3 -3
  34. package/dist/stream-broker/remoteEntry.js +1 -1
  35. package/package.json +4 -2
  36. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-aLQXb88Y.mjs +0 -26
  37. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CEiM-boT.mjs +0 -26
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- //#region ../types/dist/event-category-DHXxB8t9.mjs
2
+ //#region ../types/dist/event-category-CT-hz5Rb.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -325,6 +325,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
325
325
  /** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
326
326
  EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
327
327
  /**
328
+ * A camera's package LLM confirm has failed closed three times in a row
329
+ * (D931, 2026-10-26). One edge per camera until
330
+ * {@link PipelinePackageLlmAnswered}.
331
+ */
332
+ EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
333
+ /** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
334
+ EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
335
+ /**
328
336
  * Per-camera pipeline config was mutated by the orchestrator
329
337
  * (a settings change via `setAgentInferenceDevices` /
330
338
  * `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
@@ -5567,7 +5575,7 @@ var ZodIssueCode = {
5567
5575
  var ZodFirstPartyTypeKind;
5568
5576
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5569
5577
  //#endregion
5570
- //#region ../types/dist/sleep-CARd6wZF.mjs
5578
+ //#region ../types/dist/sleep--jXS9hWo.mjs
5571
5579
  /**
5572
5580
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5573
5581
  * window to float samples (D455).
@@ -8354,6 +8362,53 @@ var READING_QUANTITIES = [
8354
8362
  ];
8355
8363
  var ReadingQuantitySchema = _enum(READING_QUANTITIES);
8356
8364
  new Set(READING_QUANTITIES);
8365
+ /**
8366
+ * Stored unit symbol for a quantity. `none` means the number is dimensionless
8367
+ * (or an index / ratio / count). `device` means the cap has no single unit.
8368
+ */
8369
+ var QUANTITY_BASE = {
8370
+ temperature: "°C",
8371
+ pressure: "hPa",
8372
+ speed: "m/s",
8373
+ precipitation: "mm",
8374
+ "precipitation-rate": "mm/h",
8375
+ length: "m",
8376
+ area: "m²",
8377
+ illuminance: "lx",
8378
+ irradiance: "W/m²",
8379
+ power: "W",
8380
+ energy: "kWh",
8381
+ voltage: "V",
8382
+ current: "A",
8383
+ "apparent-power": "VA",
8384
+ humidity: "%",
8385
+ percentage: "%",
8386
+ "concentration-volume": "ppm",
8387
+ "concentration-mass": "µg/m³",
8388
+ "concentration-ppb": "ppb",
8389
+ volume: "L",
8390
+ duration: "s",
8391
+ "duration-ms": "ms",
8392
+ information: "B",
8393
+ "sound-level": "dBFS",
8394
+ "signal-level": "dBm",
8395
+ "colour-temperature": "mired",
8396
+ angle: "°",
8397
+ bitrate: "kbps",
8398
+ rate: "fps",
8399
+ mass: "kg",
8400
+ index: "none",
8401
+ ratio: "none",
8402
+ count: "none",
8403
+ none: "none",
8404
+ device: "device"
8405
+ };
8406
+ /** The stored unit, or `undefined` when the quantity has no unit symbol. */
8407
+ function baseUnitOf(quantity) {
8408
+ const symbol = QUANTITY_BASE[quantity];
8409
+ if (symbol === "none" || symbol === "device") return void 0;
8410
+ return symbol;
8411
+ }
8357
8412
  function readingMeta(quantity, extras) {
8358
8413
  return {
8359
8414
  quantity,
@@ -9052,6 +9107,9 @@ var CAP_NAMES = Object.freeze([
9052
9107
  "filesystem-browse",
9053
9108
  "flood",
9054
9109
  "gas",
9110
+ "group-active",
9111
+ "group-commands",
9112
+ "group-status",
9055
9113
  "heating-valve",
9056
9114
  "humidifier",
9057
9115
  "humidity-sensor",
@@ -9072,6 +9130,7 @@ var CAP_NAMES = Object.freeze([
9072
9130
  "login-method",
9073
9131
  "media-player",
9074
9132
  "mesh-network",
9133
+ "meter",
9075
9134
  "metrics-provider",
9076
9135
  "model-convert",
9077
9136
  "model-distributor",
@@ -9870,6 +9929,24 @@ Object.freeze({
9870
9929
  "path": "detected",
9871
9930
  "valueType": "boolean"
9872
9931
  }],
9932
+ "group-active": [{
9933
+ "path": "active",
9934
+ "valueType": "boolean"
9935
+ }],
9936
+ "group-status": [
9937
+ {
9938
+ "path": "included",
9939
+ "valueType": "number"
9940
+ },
9941
+ {
9942
+ "path": "excluded",
9943
+ "valueType": "number"
9944
+ },
9945
+ {
9946
+ "path": "total",
9947
+ "valueType": "number"
9948
+ }
9949
+ ],
9873
9950
  "heating-valve": [{
9874
9951
  "path": "position",
9875
9952
  "valueType": "number"
@@ -10115,6 +10192,10 @@ Object.freeze({
10115
10192
  ]
10116
10193
  }
10117
10194
  ],
10195
+ "meter": [{
10196
+ "path": "value",
10197
+ "valueType": "number"
10198
+ }],
10118
10199
  "motion": [
10119
10200
  {
10120
10201
  "path": "detected",
@@ -11010,7 +11091,7 @@ function sleep(ms) {
11010
11091
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11011
11092
  }
11012
11093
  //#endregion
11013
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
11094
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
11014
11095
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
11015
11096
  DeviceType["Camera"] = "camera";
11016
11097
  DeviceType["Hub"] = "hub";
@@ -11104,8 +11185,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
11104
11185
  DeviceType["PetFeeder"] = "pet-feeder";
11105
11186
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
11106
11187
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
11107
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
11188
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
11189
+ * A plug-built dishwasher carries running-state and power instead. */
11108
11190
  DeviceType["Dishwasher"] = "dishwasher";
11191
+ /** Washing machine. A plug-built one is running while its power stays
11192
+ * above a threshold. No program cap — that surface is the dishwasher's. */
11193
+ DeviceType["Washer"] = "washer";
11194
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
11195
+ DeviceType["Dryer"] = "dryer";
11109
11196
  return DeviceType;
11110
11197
  }({});
11111
11198
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11363,7 +11450,7 @@ function errMsg(err) {
11363
11450
  return String(err);
11364
11451
  }
11365
11452
  //#endregion
11366
- //#region ../types/dist/composition-kEl9mevu.mjs
11453
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
11367
11454
  var AUDIO_ENCODER_BY_CODEC = {
11368
11455
  opus: "libopus",
11369
11456
  aac: "aac",
@@ -12514,6 +12601,13 @@ var table = {
12514
12601
  apply: () => {
12515
12602
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
12516
12603
  }
12604
+ },
12605
+ schmitt: {
12606
+ minArgs: 3,
12607
+ maxArgs: 5,
12608
+ apply: () => {
12609
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
12610
+ }
12517
12611
  }
12518
12612
  };
12519
12613
  Object.freeze(Object.assign(Object.create(null), table));
@@ -12954,15 +13048,232 @@ function validateExpressionSource(src) {
12954
13048
  }
12955
13049
  return null;
12956
13050
  }
12957
- /** A composed device's stableId is this prefix plus the block id. */
12958
- var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
12959
- /** Longest snippet a `code` source may carry (slice 4). */
12960
- var MAX_COMPOSITION_CODE_LENGTH = 2e4;
13051
+ /**
13052
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
13053
+ *
13054
+ * Standby of the control board is under 5 W; a motor or a heater is far
13055
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
13056
+ * a cycle, so running starts only after 30 s above the line.
13057
+ *
13058
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
13059
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
13060
+ * report "finished" in the middle of the program, so the stop dwell is
13061
+ * 10 minutes on every type. The number is stored per type so one machine
13062
+ * can move later without moving the others.
13063
+ */
13064
+ var APPLIANCE_TEMPLATE_ID = "appliance";
13065
+ var APPLIANCE_KINDS = [
13066
+ "washer",
13067
+ "dryer",
13068
+ "dishwasher"
13069
+ ];
13070
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
13071
+ /**
13072
+ * Statistics and integrals over a history read (D944).
13073
+ *
13074
+ * Pure: the same points always give the same answer, so a restart recomputes
13075
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
13076
+ * never the mean of the samples. A hole with no held value is not integrated
13077
+ * as zero. A truncated read is not published.
13078
+ */
13079
+ var STATISTIC_STATS = [
13080
+ "min",
13081
+ "max",
13082
+ "mean",
13083
+ "sum",
13084
+ "count",
13085
+ "change"
13086
+ ];
13087
+ /**
13088
+ * The range a statistics or integral helper asks history for (D944).
13089
+ *
13090
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
13091
+ * or 25 hours across a DST change. A tier is eligible only when its retention
13092
+ * is strictly longer than the longest that range can be. Forever qualifies.
13093
+ * Equal to a tier's retention is a refusal, not a short answer.
13094
+ */
13095
+ var MINUTE_MS = 6e4;
13096
+ var HOUR_MS = 60 * MINUTE_MS;
13097
+ var DAY_MS = 24 * HOUR_MS;
13098
+ var ROLLING_SPANS = [
13099
+ "1h",
13100
+ "24h",
13101
+ "2d",
13102
+ "7d",
13103
+ "30d",
13104
+ "custom"
13105
+ ];
13106
+ var CALENDAR_PERIODS = [
13107
+ "today",
13108
+ "yesterday",
13109
+ "this-week",
13110
+ "this-month"
13111
+ ];
13112
+ var RESET_PERIODS = [
13113
+ "never",
13114
+ "daily",
13115
+ "weekly",
13116
+ "monthly"
13117
+ ];
13118
+ 5 * MINUTE_MS;
13119
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
13120
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
13121
+ var rollingWindowObject = object({
13122
+ kind: literal("rolling"),
13123
+ span: _enum(ROLLING_SPANS),
13124
+ customMinutes: number().int().min(5).optional()
13125
+ }).check((payload) => {
13126
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
13127
+ code: "custom",
13128
+ message: "a custom range needs a duration in minutes",
13129
+ path: ["customMinutes"],
13130
+ input: payload.value
13131
+ });
13132
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
13133
+ code: "custom",
13134
+ message: "only a custom range carries a duration",
13135
+ path: ["customMinutes"],
13136
+ input: payload.value
13137
+ });
13138
+ });
13139
+ var RollingWindowSchema = rollingWindowObject;
13140
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
13141
+ kind: literal("calendar"),
13142
+ period: _enum(CALENDAR_PERIODS)
13143
+ })]);
13144
+ /**
13145
+ * A composition source identity: `(addonId, stableId)`, never a numeric device
13146
+ * id. Split out so helper schemas can name it without a cycle through
13147
+ * `composition.ts` (D659).
13148
+ */
12961
13149
  var CompositionSourceRefSchema = object({
12962
13150
  /** The owning addon; `stableId` is unique only within it. */
12963
13151
  addonId: string().min(1),
12964
13152
  stableId: string().min(1)
12965
13153
  });
13154
+ var GROUP_TEMPLATE_IDS = [
13155
+ "temperature",
13156
+ "humidity",
13157
+ "pressure",
13158
+ "illuminance",
13159
+ "power",
13160
+ "energy",
13161
+ "battery",
13162
+ "numeric",
13163
+ "motion",
13164
+ "contact",
13165
+ "leak",
13166
+ "smoke",
13167
+ "occupancy",
13168
+ "binary",
13169
+ "switch",
13170
+ "light",
13171
+ "fan",
13172
+ "cover",
13173
+ "valve",
13174
+ "lock",
13175
+ "media-player",
13176
+ "climate"
13177
+ ];
13178
+ var NUMERIC_AGGREGATES = [
13179
+ "mean",
13180
+ "median",
13181
+ "min",
13182
+ "max",
13183
+ "sum"
13184
+ ];
13185
+ var BINARY_AGGREGATES = ["any", "all"];
13186
+ /** How an actuator group decides it is on. Unknown members never count as off. */
13187
+ var ACTIVE_CRITERIA = ["at-least-one", "all"];
13188
+ var AGGREGATE_REDUCERS = [
13189
+ "mean",
13190
+ "median",
13191
+ "min",
13192
+ "max",
13193
+ "sum",
13194
+ "any",
13195
+ "all"
13196
+ ];
13197
+ var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
13198
+ var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
13199
+ var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
13200
+ /** One member. The field is stored so occupancy can name motion or binary. */
13201
+ var HelperMemberSchema = object({
13202
+ source: CompositionSourceRefSchema,
13203
+ cap: string().min(1),
13204
+ fieldPath: string().min(1)
13205
+ });
13206
+ var HelperSpecSchema = discriminatedUnion("kind", [
13207
+ object({
13208
+ kind: literal("group"),
13209
+ templateId: GroupTemplateIdSchema,
13210
+ aggregate: AggregateReducerSchema,
13211
+ members: array(HelperMemberSchema).min(1).max(64),
13212
+ /** Actuator groups only. Readings leave this unset. */
13213
+ active: ActiveCriterionSchema.optional()
13214
+ }),
13215
+ object({
13216
+ kind: literal("appliance"),
13217
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
13218
+ appliance: _enum(APPLIANCE_KINDS),
13219
+ thresholdWatts: number().positive(),
13220
+ startDwellMs: number().int().nonnegative(),
13221
+ stopDwellMs: number().int().nonnegative(),
13222
+ member: HelperMemberSchema
13223
+ }),
13224
+ object({
13225
+ kind: literal("statistic"),
13226
+ stat: _enum(STATISTIC_STATS),
13227
+ window: StatisticWindowSchema,
13228
+ source: HelperMemberSchema
13229
+ }),
13230
+ object({
13231
+ kind: literal("integral"),
13232
+ reset: _enum(RESET_PERIODS),
13233
+ output: _enum(["energy", "meter"]),
13234
+ source: HelperMemberSchema
13235
+ }),
13236
+ object({
13237
+ kind: literal("threshold"),
13238
+ source: HelperMemberSchema,
13239
+ high: number(),
13240
+ low: number(),
13241
+ onForMs: number().int().nonnegative(),
13242
+ offForMs: number().int().nonnegative()
13243
+ }),
13244
+ object({
13245
+ kind: literal("arithmetic"),
13246
+ op: _enum([
13247
+ "sum",
13248
+ "difference",
13249
+ "ratio"
13250
+ ]),
13251
+ inputs: array(HelperMemberSchema).min(2).max(64)
13252
+ }),
13253
+ object({
13254
+ kind: literal("expression"),
13255
+ expr: string().min(1),
13256
+ inputs: array(object({
13257
+ name: string().min(1),
13258
+ member: HelperMemberSchema
13259
+ })).min(1).max(64),
13260
+ output: _enum(["number", "boolean"])
13261
+ }),
13262
+ object({
13263
+ kind: literal("derivative"),
13264
+ window: RollingWindowSchema,
13265
+ per: _enum([
13266
+ "second",
13267
+ "minute",
13268
+ "hour"
13269
+ ]),
13270
+ source: HelperMemberSchema
13271
+ })
13272
+ ]);
13273
+ /** A composed device's stableId is this prefix plus the block id. */
13274
+ var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
13275
+ /** Longest snippet a `code` source may carry (slice 4). */
13276
+ var MAX_COMPOSITION_CODE_LENGTH = 2e4;
12966
13277
  /** One field of one capability of one source device. */
12967
13278
  var CompositionFieldReadSchema = object({
12968
13279
  source: CompositionSourceRefSchema,
@@ -13006,7 +13317,59 @@ var CompositionCodeSourceSchema = object({
13006
13317
  var CompositionFieldSourceSchema = discriminatedUnion("kind", [
13007
13318
  CompositionFromSourceSchema,
13008
13319
  CompositionExpressionSourceSchema,
13009
- CompositionCodeSourceSchema
13320
+ CompositionCodeSourceSchema,
13321
+ object({
13322
+ kind: literal("aggregate"),
13323
+ reducer: _enum([
13324
+ "mean",
13325
+ "median",
13326
+ "min",
13327
+ "max",
13328
+ "sum",
13329
+ "any",
13330
+ "all"
13331
+ ]),
13332
+ reads: array(CompositionFieldReadSchema).min(1).max(64),
13333
+ /**
13334
+ * Set when the reads are an enum state, not a yes/no: the listed states
13335
+ * count as active (a cover is `open`). A state outside the list is inactive;
13336
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
13337
+ */
13338
+ activeWhen: array(string().min(1)).min(1).optional(),
13339
+ /** `value` is the reading; the others are the member counts. */
13340
+ publish: _enum([
13341
+ "value",
13342
+ "included",
13343
+ "excluded",
13344
+ "total"
13345
+ ]),
13346
+ /** What a `value` publish does when nobody is included. Counts ignore this. */
13347
+ empty: _enum([
13348
+ "null",
13349
+ "omit",
13350
+ "offline"
13351
+ ])
13352
+ }),
13353
+ object({
13354
+ kind: literal("series"),
13355
+ read: CompositionFieldReadSchema,
13356
+ /** `value` is the statistic or the integral. The others are the gap counts. */
13357
+ publish: _enum([
13358
+ "value",
13359
+ "included",
13360
+ "excluded",
13361
+ "total"
13362
+ ]),
13363
+ /**
13364
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
13365
+ * device unseeded until history answers, so a restart cannot publish 0.
13366
+ */
13367
+ empty: _enum([
13368
+ "null",
13369
+ "omit",
13370
+ "offline"
13371
+ ])
13372
+ })
13010
13373
  ]);
13011
13374
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
13012
13375
  kind: literal("forward"),
@@ -13052,7 +13415,13 @@ var CompositionSchema = object({
13052
13415
  kind: literal("existing"),
13053
13416
  device: CompositionSourceRefSchema
13054
13417
  })]),
13055
- features: array(CompositionFeatureSchema).min(1).max(16)
13418
+ features: array(CompositionFeatureSchema).min(1).max(16),
13419
+ /**
13420
+ * Present when the block is a helper template. Absent on a free
13421
+ * composition (the room example, device 4603). The editor reads this,
13422
+ * never the compiled features.
13423
+ */
13424
+ helper: HelperSpecSchema.optional()
13056
13425
  }).superRefine((composition, ctx) => {
13057
13426
  const seen = /* @__PURE__ */ new Set();
13058
13427
  composition.features.forEach((feature, index) => {
@@ -20983,7 +21352,11 @@ var CompositionProblemSchema = object({
20983
21352
  "target-missing",
20984
21353
  "target-unreadable",
20985
21354
  "not-replaceable",
20986
- "native-capability-replace"
21355
+ "native-capability-replace",
21356
+ "mixed-quantity",
21357
+ "helper-mismatch",
21358
+ "member-constraint",
21359
+ "retention"
20987
21360
  ]),
20988
21361
  path: string(),
20989
21362
  message: string()
@@ -21004,7 +21377,8 @@ var CompositionUnavailableModeSchema = _enum([
21004
21377
  "null",
21005
21378
  "offline",
21006
21379
  "unknown",
21007
- "release"
21380
+ "release",
21381
+ "omit"
21008
21382
  ]);
21009
21383
  /**
21010
21384
  * `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
@@ -21216,8 +21590,392 @@ Object.freeze({
21216
21590
  latchedAt: {
21217
21591
  kind: "fixed",
21218
21592
  result: ["number", "null"]
21593
+ },
21594
+ schmitt: {
21595
+ kind: "fixed",
21596
+ result: ["boolean", "null"]
21219
21597
  }
21220
21598
  });
21599
+ /**
21600
+ * One catalog row per base reading a group helper can build (D944 §3).
21601
+ * Data, not a form default written into the block.
21602
+ */
21603
+ var NOW$2 = {
21604
+ fieldPath: "lastFetchedAt",
21605
+ expr: "now"
21606
+ };
21607
+ var CHANGED = {
21608
+ fieldPath: "lastChangedAt",
21609
+ expr: "now"
21610
+ };
21611
+ function numeric(input) {
21612
+ return {
21613
+ ...input,
21614
+ family: "numeric",
21615
+ deviceType: DeviceType.Sensor,
21616
+ memberDeviceType: DeviceType.Sensor,
21617
+ memberDeviceTypes: [DeviceType.Sensor],
21618
+ activeWhen: void 0,
21619
+ commandCaps: [],
21620
+ baseUnit: baseUnitOf(input.quantity),
21621
+ aggregates: NUMERIC_AGGREGATES,
21622
+ accept: [{
21623
+ cap: input.outputCap,
21624
+ fieldPath: input.outputField
21625
+ }]
21626
+ };
21627
+ }
21628
+ function binary(input) {
21629
+ return {
21630
+ ...input,
21631
+ family: "binary",
21632
+ deviceType: DeviceType.Sensor,
21633
+ memberDeviceType: DeviceType.Sensor,
21634
+ memberDeviceTypes: [DeviceType.Sensor],
21635
+ activeWhen: void 0,
21636
+ commandCaps: [],
21637
+ quantity: "none",
21638
+ baseUnit: void 0,
21639
+ aggregates: BINARY_AGGREGATES,
21640
+ empty: "offline"
21641
+ };
21642
+ }
21643
+ var GROUP_TEMPLATES = [
21644
+ numeric({
21645
+ id: "temperature",
21646
+ summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
21647
+ role: DeviceRole.TemperatureSensor,
21648
+ outputCap: "temperature-sensor",
21649
+ outputField: "celsius",
21650
+ quantity: "temperature",
21651
+ defaultAggregate: "mean",
21652
+ empty: "null",
21653
+ companions: [NOW$2]
21654
+ }),
21655
+ numeric({
21656
+ id: "humidity",
21657
+ summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
21658
+ role: DeviceRole.HumiditySensor,
21659
+ outputCap: "humidity-sensor",
21660
+ outputField: "percent",
21661
+ quantity: "humidity",
21662
+ defaultAggregate: "mean",
21663
+ empty: "null",
21664
+ companions: [NOW$2]
21665
+ }),
21666
+ numeric({
21667
+ id: "pressure",
21668
+ summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
21669
+ role: DeviceRole.PressureSensor,
21670
+ outputCap: "pressure-sensor",
21671
+ outputField: "hpa",
21672
+ quantity: "pressure",
21673
+ defaultAggregate: "mean",
21674
+ empty: "null",
21675
+ companions: [NOW$2]
21676
+ }),
21677
+ numeric({
21678
+ id: "illuminance",
21679
+ summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
21680
+ role: DeviceRole.AmbientLightSensor,
21681
+ outputCap: "ambient-light-sensor",
21682
+ outputField: "lux",
21683
+ quantity: "illuminance",
21684
+ defaultAggregate: "mean",
21685
+ empty: "null",
21686
+ companions: [NOW$2]
21687
+ }),
21688
+ numeric({
21689
+ id: "power",
21690
+ summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
21691
+ role: DeviceRole.PowerSensor,
21692
+ outputCap: "power-meter",
21693
+ outputField: "watts",
21694
+ quantity: "power",
21695
+ defaultAggregate: "sum",
21696
+ empty: "omit",
21697
+ companions: [NOW$2]
21698
+ }),
21699
+ numeric({
21700
+ id: "energy",
21701
+ summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
21702
+ role: DeviceRole.EnergySensor,
21703
+ outputCap: "power-meter",
21704
+ outputField: "kwhTotal",
21705
+ quantity: "energy",
21706
+ defaultAggregate: "sum",
21707
+ empty: "omit",
21708
+ companions: [NOW$2]
21709
+ }),
21710
+ numeric({
21711
+ id: "battery",
21712
+ summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
21713
+ role: DeviceRole.BatterySensor,
21714
+ outputCap: "battery",
21715
+ outputField: "percentage",
21716
+ quantity: "percentage",
21717
+ defaultAggregate: "mean",
21718
+ empty: "null",
21719
+ companions: [
21720
+ {
21721
+ fieldPath: "charging",
21722
+ expr: "\"unknown\""
21723
+ },
21724
+ {
21725
+ fieldPath: "sleeping",
21726
+ expr: "false"
21727
+ },
21728
+ {
21729
+ fieldPath: "lastUpdated",
21730
+ expr: "now"
21731
+ }
21732
+ ]
21733
+ }),
21734
+ numeric({
21735
+ id: "numeric",
21736
+ summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
21737
+ role: DeviceRole.NumericSensor,
21738
+ outputCap: "numeric-sensor",
21739
+ outputField: "value",
21740
+ quantity: "device",
21741
+ defaultAggregate: "mean",
21742
+ empty: "null",
21743
+ companions: [NOW$2]
21744
+ }),
21745
+ binary({
21746
+ id: "motion",
21747
+ summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
21748
+ role: DeviceRole.MotionSensor,
21749
+ outputCap: "motion",
21750
+ outputField: "detected",
21751
+ defaultAggregate: "any",
21752
+ accept: [{
21753
+ cap: "motion",
21754
+ fieldPath: "detected"
21755
+ }],
21756
+ companions: [{
21757
+ fieldPath: "lastDetectedAt",
21758
+ expr: "null"
21759
+ }, {
21760
+ fieldPath: "autoClearAfterMs",
21761
+ expr: "null"
21762
+ }]
21763
+ }),
21764
+ binary({
21765
+ id: "contact",
21766
+ summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
21767
+ role: DeviceRole.ContactSensor,
21768
+ outputCap: "contact",
21769
+ outputField: "entryOpen",
21770
+ defaultAggregate: "any",
21771
+ accept: [{
21772
+ cap: "contact",
21773
+ fieldPath: "entryOpen"
21774
+ }],
21775
+ companions: [CHANGED]
21776
+ }),
21777
+ binary({
21778
+ id: "leak",
21779
+ summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
21780
+ role: DeviceRole.LeakSensor,
21781
+ outputCap: "flood",
21782
+ outputField: "flooded",
21783
+ defaultAggregate: "any",
21784
+ accept: [{
21785
+ cap: "flood",
21786
+ fieldPath: "flooded"
21787
+ }],
21788
+ companions: [CHANGED]
21789
+ }),
21790
+ binary({
21791
+ id: "smoke",
21792
+ summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
21793
+ role: DeviceRole.SmokeSensor,
21794
+ outputCap: "smoke",
21795
+ outputField: "detected",
21796
+ defaultAggregate: "any",
21797
+ accept: [{
21798
+ cap: "smoke",
21799
+ fieldPath: "detected"
21800
+ }],
21801
+ companions: [CHANGED]
21802
+ }),
21803
+ binary({
21804
+ id: "occupancy",
21805
+ summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
21806
+ role: DeviceRole.BinarySensor,
21807
+ outputCap: "binary",
21808
+ outputField: "on",
21809
+ defaultAggregate: "any",
21810
+ accept: [{
21811
+ cap: "motion",
21812
+ fieldPath: "detected"
21813
+ }, {
21814
+ cap: "binary",
21815
+ fieldPath: "on"
21816
+ }],
21817
+ companions: [CHANGED]
21818
+ }),
21819
+ binary({
21820
+ id: "binary",
21821
+ summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
21822
+ role: DeviceRole.BinarySensor,
21823
+ outputCap: "binary",
21824
+ outputField: "on",
21825
+ defaultAggregate: "any",
21826
+ accept: [{
21827
+ cap: "binary",
21828
+ fieldPath: "on"
21829
+ }],
21830
+ companions: [CHANGED]
21831
+ }),
21832
+ actuator({
21833
+ id: "switch",
21834
+ summary: "On when at least one switch is on, or when all of them are. Offline and unknown members are left out of that decision, never read as off, and still receive the command.",
21835
+ deviceType: DeviceType.Switch,
21836
+ outputCap: "switch",
21837
+ outputField: "on",
21838
+ member: {
21839
+ cap: "switch",
21840
+ fieldPath: "on"
21841
+ },
21842
+ commandCaps: ["switch"]
21843
+ }),
21844
+ actuator({
21845
+ id: "light",
21846
+ summary: "On when at least one light is on, or when all of them are. Brightness and colour are offered only when every member has them. A command is not sent to a member that lacks it.",
21847
+ deviceType: DeviceType.Light,
21848
+ outputCap: "switch",
21849
+ outputField: "on",
21850
+ member: {
21851
+ cap: "switch",
21852
+ fieldPath: "on"
21853
+ },
21854
+ commandCaps: [
21855
+ "switch",
21856
+ "brightness",
21857
+ "color"
21858
+ ]
21859
+ }),
21860
+ actuator({
21861
+ id: "fan",
21862
+ summary: "On when at least one fan is on, or when all of them are. Speed, preset, direction and oscillation are offered only when every fan has them. A command is not sent to a fan that lacks it.",
21863
+ deviceType: DeviceType.Fan,
21864
+ outputCap: "switch",
21865
+ outputField: "on",
21866
+ member: {
21867
+ cap: "switch",
21868
+ fieldPath: "on"
21869
+ },
21870
+ commandCaps: ["switch", "fan-control"]
21871
+ }),
21872
+ actuator({
21873
+ id: "cover",
21874
+ summary: "Open when at least one cover is open or part-open, or only when all of them are. Position and tilt are offered only when every cover has them. A cover that reports nothing is left out, never read as closed.",
21875
+ deviceType: DeviceType.Cover,
21876
+ outputCap: "group-active",
21877
+ outputField: "active",
21878
+ member: {
21879
+ cap: "cover",
21880
+ fieldPath: "state"
21881
+ },
21882
+ activeWhen: [
21883
+ "open",
21884
+ "opening",
21885
+ "stopped"
21886
+ ],
21887
+ commandCaps: ["cover"]
21888
+ }),
21889
+ actuator({
21890
+ id: "valve",
21891
+ summary: "Open when at least one valve is open or part-open, or only when all of them are. Position is offered only when every valve has it. A valve that reports nothing is left out, never read as closed.",
21892
+ deviceType: DeviceType.Valve,
21893
+ outputCap: "group-active",
21894
+ outputField: "active",
21895
+ member: {
21896
+ cap: "valve",
21897
+ fieldPath: "state"
21898
+ },
21899
+ activeWhen: [
21900
+ "open",
21901
+ "opening",
21902
+ "stopped"
21903
+ ],
21904
+ commandCaps: ["valve"]
21905
+ }),
21906
+ actuator({
21907
+ id: "lock",
21908
+ summary: "Locked when at least one lock is locked, or only when all of them are. A jammed or moving lock is not locked. Locking is one tap; unlocking the group needs a confirmation every time, and nothing is sent without it.",
21909
+ deviceType: DeviceType.Lock,
21910
+ outputCap: "group-active",
21911
+ outputField: "active",
21912
+ member: {
21913
+ cap: "lock-control",
21914
+ fieldPath: "state"
21915
+ },
21916
+ activeWhen: ["locked"],
21917
+ commandCaps: ["lock-control"]
21918
+ }),
21919
+ actuator({
21920
+ id: "media-player",
21921
+ summary: "Playing when at least one player is playing, or only when all of them are. Volume is offered only when every player has it. A player that reports nothing is left out, never read as stopped.",
21922
+ deviceType: DeviceType.MediaPlayer,
21923
+ outputCap: "group-active",
21924
+ outputField: "active",
21925
+ member: {
21926
+ cap: "media-player",
21927
+ fieldPath: "state"
21928
+ },
21929
+ activeWhen: ["playing", "buffering"],
21930
+ commandCaps: ["media-player"]
21931
+ }),
21932
+ actuator({
21933
+ id: "climate",
21934
+ summary: "Running when at least one thermostat or air conditioner is not off, or only when all of them are. Target temperature and fan are offered only when every device has them. A device that reports nothing is left out, never read as off.",
21935
+ deviceType: DeviceType.Thermostat,
21936
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
21937
+ outputCap: "group-active",
21938
+ outputField: "active",
21939
+ member: {
21940
+ cap: "climate-control",
21941
+ fieldPath: "mode"
21942
+ },
21943
+ activeWhen: [
21944
+ "heat",
21945
+ "cool",
21946
+ "auto",
21947
+ "heat_cool",
21948
+ "fan_only",
21949
+ "dry"
21950
+ ],
21951
+ commandCaps: ["climate-control"]
21952
+ })
21953
+ ];
21954
+ function actuator(input) {
21955
+ return {
21956
+ id: input.id,
21957
+ summary: input.summary,
21958
+ deviceType: input.deviceType,
21959
+ outputCap: input.outputCap,
21960
+ outputField: input.outputField,
21961
+ commandCaps: input.commandCaps,
21962
+ family: "actuator",
21963
+ role: void 0,
21964
+ memberDeviceType: input.deviceType,
21965
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
21966
+ activeWhen: input.activeWhen,
21967
+ quantity: "none",
21968
+ baseUnit: void 0,
21969
+ aggregates: BINARY_AGGREGATES,
21970
+ defaultAggregate: "any",
21971
+ empty: "offline",
21972
+ accept: [input.member],
21973
+ companions: [CHANGED]
21974
+ };
21975
+ }
21976
+ new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
21977
+ DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
21978
+ DeviceType.Camera;
21221
21979
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
21222
21980
  /**
21223
21981
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -48229,6 +48987,8 @@ var NcSystemEventKindSchema = _enum([
48229
48987
  "detection-blind",
48230
48988
  "motion-noisy",
48231
48989
  "motion-calm",
48990
+ "package-llm-silent",
48991
+ "package-llm-answered",
48232
48992
  "recording-stalled",
48233
48993
  "recording-healthy",
48234
48994
  "recording-degraded",
@@ -49318,7 +50078,20 @@ var NcRuleInputSchema = object({
49318
50078
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
49319
50079
  * path does not run Zod defaults.
49320
50080
  */
49321
- groupingSec: number().int().min(5).max(60).optional()
50081
+ groupingSec: number().int().min(5).max(60).optional(),
50082
+ /**
50083
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
50084
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
50085
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
50086
+ * same mark the Library writes, and records whose notification it was and
50087
+ * when. A channel with no buttons gets the same two choices as links.
50088
+ *
50089
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
50090
+ * cap path): absent and `false` both mean off, which is what every rule
50091
+ * authored before this field meant. An editor turns it off by writing
50092
+ * `false`, so it needs no `clear` key.
50093
+ */
50094
+ askVerdict: boolean().optional()
49322
50095
  });
49323
50096
  /**
49324
50097
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -60083,6 +60856,28 @@ var PackageVersionInfoSchema = object({
60083
60856
  deprecated: string().optional(),
60084
60857
  distTags: array(string()).readonly()
60085
60858
  });
60859
+ /**
60860
+ * One published document. `ok` is markdown from the registry. `missing` means
60861
+ * the registry answered and the file was not there. `error` is a registry or
60862
+ * tarball failure — never an empty string standing in for either.
60863
+ */
60864
+ var PackageDocBodySchema = discriminatedUnion("status", [
60865
+ object({
60866
+ status: literal("ok"),
60867
+ markdown: string()
60868
+ }),
60869
+ object({ status: literal("missing") }),
60870
+ object({
60871
+ status: literal("error"),
60872
+ message: string()
60873
+ })
60874
+ ]);
60875
+ var PackageDocsSchema = object({
60876
+ name: string(),
60877
+ version: string(),
60878
+ readme: PackageDocBodySchema,
60879
+ changelog: PackageDocBodySchema
60880
+ });
60086
60881
  var SearchResultSchema = object({
60087
60882
  name: string(),
60088
60883
  version: string(),
@@ -60301,7 +61096,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
60301
61096
  }), object({ success: literal(true) }), {
60302
61097
  kind: "mutation",
60303
61098
  auth: "admin"
60304
- }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
61099
+ }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
61100
+ name: string().min(1),
61101
+ version: string().min(1).optional()
61102
+ }), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
60305
61103
  kind: "mutation",
60306
61104
  auth: "admin"
60307
61105
  }), method(object({ packageName: string() }), object({ success: literal(true) }), {
@@ -62467,6 +63265,42 @@ object({
62467
63265
  lastChangedAt: number()
62468
63266
  });
62469
63267
  DeviceType.Sensor;
63268
+ object({
63269
+ /** One row per member that has been commanded. A later command replaces that member's row. */
63270
+ results: array(object({
63271
+ deviceId: number().int().nonnegative(),
63272
+ cap: string().min(1),
63273
+ method: string().min(1),
63274
+ ok: boolean(),
63275
+ error: string().optional(),
63276
+ /** Ms epoch of the attempt. */
63277
+ at: number()
63278
+ })) });
63279
+ DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
63280
+ object({
63281
+ active: boolean().meta({ label: "Active" }),
63282
+ /** Ms epoch when the slice was last updated. */
63283
+ lastChangedAt: number()
63284
+ });
63285
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
63286
+ object({
63287
+ /** Members whose current value entered the aggregate. */
63288
+ included: number().int().nonnegative().meta(readingMeta("count", {
63289
+ precision: 0,
63290
+ label: "Reporting"
63291
+ })),
63292
+ /** Members left out: offline, unknown, or not a finite reading (D393). */
63293
+ excluded: number().int().nonnegative().meta(readingMeta("count", {
63294
+ precision: 0,
63295
+ label: "Excluded"
63296
+ })),
63297
+ /** `included` + `excluded`. */
63298
+ total: number().int().nonnegative().meta(readingMeta("count", {
63299
+ precision: 0,
63300
+ label: "Members"
63301
+ }))
63302
+ });
63303
+ DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
62470
63304
  object({
62471
63305
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
62472
63306
  position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -63373,7 +64207,14 @@ DeviceType.Lock, method(object({
63373
64207
  auth: "protected"
63374
64208
  }), method(object({
63375
64209
  deviceId: number().int().nonnegative(),
63376
- code: string().min(1).optional()
64210
+ code: string().min(1).optional(),
64211
+ /**
64212
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
64213
+ * with one call is refused unless the caller says it was confirmed. A
64214
+ * single lock ignores it. The refusal lives in the callee, so no
64215
+ * automation or API caller reaches the doors by skipping a dialog.
64216
+ */
64217
+ confirm: boolean().optional()
63377
64218
  }), _void(), {
63378
64219
  kind: "mutation",
63379
64220
  auth: "protected"
@@ -63381,6 +64222,11 @@ DeviceType.Lock, method(object({
63381
64222
  kind: "mutation",
63382
64223
  auth: "protected"
63383
64224
  });
64225
+ object({
64226
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
64227
+ lastFetchedAt: number()
64228
+ });
64229
+ DeviceType.Sensor;
63384
64230
  /**
63385
64231
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
63386
64232
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
@@ -65298,7 +66144,7 @@ object({
65298
66144
  /** Ms epoch when the slice was last updated. */
65299
66145
  lastFetchedAt: number()
65300
66146
  });
65301
- DeviceType.Sensor;
66147
+ DeviceType.Sensor, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
65302
66148
  /**
65303
66149
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
65304
66150
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -65745,7 +66591,7 @@ object({
65745
66591
  phase: string().nullable()
65746
66592
  });
65747
66593
  Object.values(DeviceType);
65748
- DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
66594
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
65749
66595
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
65750
66596
  kind: "mutation",
65751
66597
  auth: "admin"
@@ -68308,7 +69154,7 @@ object({
68308
69154
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
68309
69155
  lastChangedAt: number()
68310
69156
  });
68311
- DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
69157
+ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
68312
69158
  deviceId: number(),
68313
69159
  on: boolean()
68314
69160
  }), _void(), {
@@ -70040,8 +70886,9 @@ function dominantClipLabels(events) {
70040
70886
  }
70041
70887
  return [...counts.entries()].toSorted((x, y) => y[1] - x[1]).slice(0, 3).map(([name]) => name);
70042
70888
  }
70889
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
70043
70890
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
70044
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Camera, DeviceType.Camera, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
70891
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
70045
70892
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
70046
70893
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
70047
70894
  new Set(CAP_NAMES);
@@ -70112,7 +70959,7 @@ function deriveBatteryPresence(input) {
70112
70959
  const budget = input.unreachableAfterMs ?? 216e5;
70113
70960
  return input.nowMs - lastContactAt > budget ? "unreachable" : "sleeping";
70114
70961
  }
70115
- DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
70962
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
70116
70963
  new Set(Object.values(DeviceType));
70117
70964
  Object.freeze({
70118
70965
  "accessories.getStatus": {
@@ -70220,6 +71067,13 @@ Object.freeze({
70220
71067
  access: "view",
70221
71068
  role: "admin"
70222
71069
  },
71070
+ "addons.getPackageDocs": {
71071
+ capName: "addons",
71072
+ capScope: "system",
71073
+ addonId: null,
71074
+ access: "view",
71075
+ role: "admin"
71076
+ },
70223
71077
  "addons.getVersions": {
70224
71078
  capName: "addons",
70225
71079
  capScope: "system",
@@ -73328,6 +74182,27 @@ Object.freeze({
73328
74182
  access: "view",
73329
74183
  role: "viewer"
73330
74184
  },
74185
+ "groupActive.getStatus": {
74186
+ capName: "group-active",
74187
+ capScope: "device",
74188
+ addonId: null,
74189
+ access: "view",
74190
+ role: "viewer"
74191
+ },
74192
+ "groupCommands.getStatus": {
74193
+ capName: "group-commands",
74194
+ capScope: "device",
74195
+ addonId: null,
74196
+ access: "view",
74197
+ role: "viewer"
74198
+ },
74199
+ "groupStatus.getStatus": {
74200
+ capName: "group-status",
74201
+ capScope: "device",
74202
+ addonId: null,
74203
+ access: "view",
74204
+ role: "viewer"
74205
+ },
73331
74206
  "heatingValve.getStatus": {
73332
74207
  capName: "heating-valve",
73333
74208
  capScope: "device",
@@ -74280,6 +75155,13 @@ Object.freeze({
74280
75155
  access: "create",
74281
75156
  role: "admin"
74282
75157
  },
75158
+ "meter.getStatus": {
75159
+ capName: "meter",
75160
+ capScope: "device",
75161
+ addonId: null,
75162
+ access: "view",
75163
+ role: "viewer"
75164
+ },
74283
75165
  "metricsProvider.collectSnapshot": {
74284
75166
  capName: "metrics-provider",
74285
75167
  capScope: "system",
@@ -82378,7 +83260,39 @@ var APPLIANCE_CAP_VOCABULARY = {
82378
83260
  phrase: t("When {device} finishes", "Quando {device} finisce"),
82379
83261
  icon: "square"
82380
83262
  }
82381
- }
83263
+ },
83264
+ overrides: { byType: {
83265
+ [DeviceType.Washer]: {
83266
+ state: { running: {
83267
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
83268
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
83269
+ } },
83270
+ events: {
83271
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
83272
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
83273
+ }
83274
+ },
83275
+ [DeviceType.Dryer]: {
83276
+ state: { running: {
83277
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
83278
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
83279
+ } },
83280
+ events: {
83281
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
83282
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
83283
+ }
83284
+ },
83285
+ [DeviceType.Dishwasher]: {
83286
+ state: { running: {
83287
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
83288
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
83289
+ } },
83290
+ events: {
83291
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
83292
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
83293
+ }
83294
+ }
83295
+ } }
82382
83296
  },
82383
83297
  update: {
82384
83298
  scope: "device",
@@ -83150,6 +84064,41 @@ var SENSOR_CAP_VOCABULARY = {
83150
84064
  icon: "flame"
83151
84065
  }]
83152
84066
  },
84067
+ "group-active": {
84068
+ scope: "device",
84069
+ noun: t("Group state", "Stato del gruppo")
84070
+ },
84071
+ "group-commands": {
84072
+ scope: "device",
84073
+ noun: t("Group command", "Comando di gruppo")
84074
+ },
84075
+ "group-status": {
84076
+ scope: "device",
84077
+ noun: t("Group", "Gruppo"),
84078
+ state: [
84079
+ {
84080
+ kind: "numeric",
84081
+ field: "included",
84082
+ quantity: t("the members reporting", "i membri che rispondono"),
84083
+ label: t("Reporting", "Rispondono"),
84084
+ icon: "users"
84085
+ },
84086
+ {
84087
+ kind: "numeric",
84088
+ field: "excluded",
84089
+ quantity: t("the members left out", "i membri esclusi"),
84090
+ label: t("Excluded", "Esclusi"),
84091
+ icon: "users"
84092
+ },
84093
+ {
84094
+ kind: "numeric",
84095
+ field: "total",
84096
+ quantity: t("the member count", "il numero di membri"),
84097
+ label: t("Members", "Membri"),
84098
+ icon: "sigma"
84099
+ }
84100
+ ]
84101
+ },
83153
84102
  "humidity-sensor": {
83154
84103
  scope: "device",
83155
84104
  noun: t("Humidity", "Umidità"),
@@ -83175,6 +84124,17 @@ var SENSOR_CAP_VOCABULARY = {
83175
84124
  }],
83176
84125
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
83177
84126
  },
84127
+ meter: {
84128
+ scope: "device",
84129
+ noun: t("Meter", "Contatore"),
84130
+ state: [{
84131
+ kind: "numeric",
84132
+ field: "value",
84133
+ quantity: t("the total", "il totale"),
84134
+ label: t("Total", "Totale"),
84135
+ icon: "gauge"
84136
+ }]
84137
+ },
83178
84138
  "numeric-sensor": {
83179
84139
  scope: "device",
83180
84140
  noun: t("Sensor", "Sensore"),
@@ -83647,8 +84607,47 @@ function projectAction(cap, action) {
83647
84607
  };
83648
84608
  }
83649
84609
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
83650
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
84610
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
83651
84611
  DeviceFeature.BatteryOperated;
84612
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
84613
+ var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
84614
+ var HOMEKIT_REASON = "HomeKit has no laundry-appliance service. The export tab would pair an accessory with nothing on it: running-state is withheld until a null reading can be unavailable, and power is not a HomeKit service this export honours.";
84615
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
84616
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
84617
+ var MATTER_REASON = "No Matter export is built. The closest device types are Laundry Washer (0x0073) and Laundry Dryer (0x0074); a power threshold is not the Laundry Washer Mode cluster.";
84618
+ function withheld(reason) {
84619
+ return {
84620
+ [DeviceType.Washer]: {
84621
+ exported: false,
84622
+ reason
84623
+ },
84624
+ [DeviceType.Dryer]: {
84625
+ exported: false,
84626
+ reason
84627
+ },
84628
+ [DeviceType.Dishwasher]: {
84629
+ exported: false,
84630
+ reason
84631
+ }
84632
+ };
84633
+ }
84634
+ function offered(received) {
84635
+ return {
84636
+ [DeviceType.Washer]: {
84637
+ exported: true,
84638
+ as: received
84639
+ },
84640
+ [DeviceType.Dryer]: {
84641
+ exported: true,
84642
+ as: received
84643
+ },
84644
+ [DeviceType.Dishwasher]: {
84645
+ exported: true,
84646
+ as: received
84647
+ }
84648
+ };
84649
+ }
84650
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
83652
84651
  Object.freeze({
83653
84652
  "accessories.getStatus": [{
83654
84653
  name: "deviceId",
@@ -84615,6 +85614,21 @@ Object.freeze({
84615
85614
  form: "single",
84616
85615
  optional: false
84617
85616
  }],
85617
+ "groupActive.getStatus": [{
85618
+ name: "deviceId",
85619
+ form: "single",
85620
+ optional: false
85621
+ }],
85622
+ "groupCommands.getStatus": [{
85623
+ name: "deviceId",
85624
+ form: "single",
85625
+ optional: false
85626
+ }],
85627
+ "groupStatus.getStatus": [{
85628
+ name: "deviceId",
85629
+ form: "single",
85630
+ optional: false
85631
+ }],
84618
85632
  "heatingValve.getStatus": [{
84619
85633
  name: "deviceId",
84620
85634
  form: "single",
@@ -84815,6 +85829,11 @@ Object.freeze({
84815
85829
  form: "single",
84816
85830
  optional: false
84817
85831
  }],
85832
+ "meter.getStatus": [{
85833
+ name: "deviceId",
85834
+ form: "single",
85835
+ optional: false
85836
+ }],
84818
85837
  "motion.getStatus": [{
84819
85838
  name: "deviceId",
84820
85839
  form: "single",
@@ -86422,7 +87441,6 @@ Object.freeze({
86422
87441
  "network-access": "ingress",
86423
87442
  "smtp-provider": "email"
86424
87443
  });
86425
- DeviceType.Camera;
86426
87444
  /**
86427
87445
  * A saved Customize assignment (D936). The schema is the type: callers use
86428
87446
  * `z.infer`, they do not restate the shape.
@@ -86649,6 +87667,8 @@ new Map(Object.entries({
86649
87667
  "detection-blind": "device-events",
86650
87668
  "motion-noisy": "device-events",
86651
87669
  "motion-calm": "device-events",
87670
+ "package-llm-silent": "device-events",
87671
+ "package-llm-answered": "device-events",
86652
87672
  "recording-stalled": "device-events",
86653
87673
  "recording-healthy": "device-events",
86654
87674
  "recording-degraded": "device-events",
@@ -86732,6 +87752,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
86732
87752
  label: "Motion without a subject",
86733
87753
  kinds: ["motion-noisy", "motion-calm"]
86734
87754
  },
87755
+ {
87756
+ id: "package-llm",
87757
+ family: "device-events",
87758
+ label: "Package confirm",
87759
+ kinds: ["package-llm-silent", "package-llm-answered"]
87760
+ },
86735
87761
  {
86736
87762
  id: "recording",
86737
87763
  family: "device-events",
@@ -86913,6 +87939,8 @@ var DEVICE_EVENTS = [
86913
87939
  "detection-blind",
86914
87940
  "motion-noisy",
86915
87941
  "motion-calm",
87942
+ "package-llm-silent",
87943
+ "package-llm-answered",
86916
87944
  "recording-stalled",
86917
87945
  "recording-healthy",
86918
87946
  "recording-degraded",
@@ -87181,6 +88209,15 @@ new Map([
87181
88209
  snapshotLabel: "Cameras with constant motion now",
87182
88210
  snapshotSection: null
87183
88211
  },
88212
+ {
88213
+ id: "package-llm-confirm",
88214
+ family: "device-events",
88215
+ label: "Package confirm (not answering / answering again)",
88216
+ bad: ["package-llm-silent"],
88217
+ recovery: ["package-llm-answered"],
88218
+ snapshotLabel: "Cameras whose package confirm is not answering",
88219
+ snapshotSection: null
88220
+ },
87184
88221
  {
87185
88222
  id: "recording-health",
87186
88223
  family: "device-events",