@camstack/types 1.2.422 → 1.2.424

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 (47) hide show
  1. package/dist/addon.js +1 -1
  2. package/dist/addon.mjs +1 -1
  3. package/dist/capabilities/composer.cap.d.ts +6 -0
  4. package/dist/capabilities/core-blocks.cap.d.ts +43 -0
  5. package/dist/capabilities/group-active.cap.d.ts +46 -0
  6. package/dist/capabilities/group-commands.cap.d.ts +1 -1
  7. package/dist/capabilities/group-status.cap.d.ts +1 -1
  8. package/dist/capabilities/index.d.ts +3 -1
  9. package/dist/capabilities/lock-control.cap.d.ts +1 -0
  10. package/dist/capabilities/notification-rules.cap.d.ts +12 -0
  11. package/dist/capabilities/switch.cap.d.ts +1 -1
  12. package/dist/client.js +17 -1
  13. package/dist/client.mjs +17 -1
  14. package/dist/composition/composition-report.d.ts +2 -0
  15. package/dist/composition/composition.d.ts +9 -0
  16. package/dist/composition/helpers/actuator-methods.d.ts +39 -0
  17. package/dist/composition/helpers/aggregate.d.ts +2 -0
  18. package/dist/composition/helpers/catalog.d.ts +7 -1
  19. package/dist/composition/helpers/compile-history.d.ts +16 -1
  20. package/dist/composition/helpers/compile-math.d.ts +9 -0
  21. package/dist/composition/helpers/history-math.d.ts +8 -0
  22. package/dist/composition/helpers/index.d.ts +6 -4
  23. package/dist/composition/helpers/source-pick.d.ts +8 -1
  24. package/dist/composition/helpers/spec.d.ts +49 -3
  25. package/dist/{composition-CpETZ4HL.mjs → composition-Bb8iwHEJ.mjs} +175 -5
  26. package/dist/{composition-CPRAP41h.js → composition-CpAc0YfQ.js} +228 -4
  27. package/dist/device/cap-vocabulary/entries/sensors.d.ts +1 -1
  28. package/dist/expression/builtins.d.ts +11 -2
  29. package/dist/expression/expression-source.d.ts +6 -1
  30. package/dist/generated/addon-api.d.ts +14 -0
  31. package/dist/generated/addon-cap-facts.d.ts +1 -1
  32. package/dist/generated/cap-status-types.d.ts +3 -1
  33. package/dist/generated/capability-router-map.d.ts +5 -2
  34. package/dist/generated/device-local-state.d.ts +3 -0
  35. package/dist/generated/device-proxy.d.ts +3 -0
  36. package/dist/generated/device-scoped-caps.d.ts +1 -1
  37. package/dist/generated/history-catalog.d.ts +1 -1
  38. package/dist/generated/method-access-map.d.ts +1 -1
  39. package/dist/generated/method-device-selectors.d.ts +1 -1
  40. package/dist/generated/runtime-state-durability.d.ts +1 -1
  41. package/dist/index.js +648 -34
  42. package/dist/index.mjs +625 -35
  43. package/dist/node.js +1 -1
  44. package/dist/node.mjs +1 -1
  45. package/dist/{sleep-0sD_LHLI.mjs → sleep--jXS9hWo.mjs} +10 -1
  46. package/dist/{sleep-CEKGW7A5.js → sleep-edKiOXLo.js} +10 -1
  47. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-CEKGW7A5.js");
2
+ const require_sleep = require("./sleep-edKiOXLo.js");
3
3
  const require_event_category = require("./event-category-Ave2VSxY.js");
4
- const require_composition = require("./composition-CPRAP41h.js");
4
+ const require_composition = require("./composition-CpAc0YfQ.js");
5
5
  const require_node_color = require("./node-color.js");
6
6
  const require_err_msg = require("./err-msg-U7lLUMNz.js");
7
7
  const require_enums = require("./enums.js");
@@ -12922,6 +12922,10 @@ var BUILTIN_RESULT_KINDS = Object.freeze({
12922
12922
  latchedAt: {
12923
12923
  kind: "fixed",
12924
12924
  result: ["number", "null"]
12925
+ },
12926
+ schmitt: {
12927
+ kind: "fixed",
12928
+ result: ["boolean", "null"]
12925
12929
  }
12926
12930
  });
12927
12931
  var KindInferenceError = class extends Error {};
@@ -13286,7 +13290,7 @@ function planItems(def, feature, featureIndex, lookupCap, mode = "new") {
13286
13290
  * One catalog row per base reading a group helper can build (D944 §3).
13287
13291
  * Data, not a form default written into the block.
13288
13292
  */
13289
- var NOW$1 = {
13293
+ var NOW$2 = {
13290
13294
  fieldPath: "lastFetchedAt",
13291
13295
  expr: "now"
13292
13296
  };
@@ -13300,6 +13304,8 @@ function numeric(input) {
13300
13304
  family: "numeric",
13301
13305
  deviceType: require_err_msg.DeviceType.Sensor,
13302
13306
  memberDeviceType: require_err_msg.DeviceType.Sensor,
13307
+ memberDeviceTypes: [require_err_msg.DeviceType.Sensor],
13308
+ activeWhen: void 0,
13303
13309
  commandCaps: [],
13304
13310
  baseUnit: require_sleep.baseUnitOf(input.quantity),
13305
13311
  aggregates: require_composition.NUMERIC_AGGREGATES,
@@ -13315,6 +13321,8 @@ function binary(input) {
13315
13321
  family: "binary",
13316
13322
  deviceType: require_err_msg.DeviceType.Sensor,
13317
13323
  memberDeviceType: require_err_msg.DeviceType.Sensor,
13324
+ memberDeviceTypes: [require_err_msg.DeviceType.Sensor],
13325
+ activeWhen: void 0,
13318
13326
  commandCaps: [],
13319
13327
  quantity: "none",
13320
13328
  baseUnit: void 0,
@@ -13332,7 +13340,7 @@ var GROUP_TEMPLATES = [
13332
13340
  quantity: "temperature",
13333
13341
  defaultAggregate: "mean",
13334
13342
  empty: "null",
13335
- companions: [NOW$1]
13343
+ companions: [NOW$2]
13336
13344
  }),
13337
13345
  numeric({
13338
13346
  id: "humidity",
@@ -13343,7 +13351,7 @@ var GROUP_TEMPLATES = [
13343
13351
  quantity: "humidity",
13344
13352
  defaultAggregate: "mean",
13345
13353
  empty: "null",
13346
- companions: [NOW$1]
13354
+ companions: [NOW$2]
13347
13355
  }),
13348
13356
  numeric({
13349
13357
  id: "pressure",
@@ -13354,7 +13362,7 @@ var GROUP_TEMPLATES = [
13354
13362
  quantity: "pressure",
13355
13363
  defaultAggregate: "mean",
13356
13364
  empty: "null",
13357
- companions: [NOW$1]
13365
+ companions: [NOW$2]
13358
13366
  }),
13359
13367
  numeric({
13360
13368
  id: "illuminance",
@@ -13365,7 +13373,7 @@ var GROUP_TEMPLATES = [
13365
13373
  quantity: "illuminance",
13366
13374
  defaultAggregate: "mean",
13367
13375
  empty: "null",
13368
- companions: [NOW$1]
13376
+ companions: [NOW$2]
13369
13377
  }),
13370
13378
  numeric({
13371
13379
  id: "power",
@@ -13376,7 +13384,7 @@ var GROUP_TEMPLATES = [
13376
13384
  quantity: "power",
13377
13385
  defaultAggregate: "sum",
13378
13386
  empty: "omit",
13379
- companions: [NOW$1]
13387
+ companions: [NOW$2]
13380
13388
  }),
13381
13389
  numeric({
13382
13390
  id: "energy",
@@ -13387,7 +13395,7 @@ var GROUP_TEMPLATES = [
13387
13395
  quantity: "energy",
13388
13396
  defaultAggregate: "sum",
13389
13397
  empty: "omit",
13390
- companions: [NOW$1]
13398
+ companions: [NOW$2]
13391
13399
  }),
13392
13400
  numeric({
13393
13401
  id: "battery",
@@ -13422,7 +13430,7 @@ var GROUP_TEMPLATES = [
13422
13430
  quantity: "device",
13423
13431
  defaultAggregate: "mean",
13424
13432
  empty: "null",
13425
- companions: [NOW$1]
13433
+ companions: [NOW$2]
13426
13434
  }),
13427
13435
  binary({
13428
13436
  id: "motion",
@@ -13516,6 +13524,11 @@ var GROUP_TEMPLATES = [
13516
13524
  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.",
13517
13525
  deviceType: require_err_msg.DeviceType.Switch,
13518
13526
  outputCap: "switch",
13527
+ outputField: "on",
13528
+ member: {
13529
+ cap: "switch",
13530
+ fieldPath: "on"
13531
+ },
13519
13532
  commandCaps: ["switch"]
13520
13533
  }),
13521
13534
  actuator({
@@ -13523,29 +13536,130 @@ var GROUP_TEMPLATES = [
13523
13536
  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.",
13524
13537
  deviceType: require_err_msg.DeviceType.Light,
13525
13538
  outputCap: "switch",
13539
+ outputField: "on",
13540
+ member: {
13541
+ cap: "switch",
13542
+ fieldPath: "on"
13543
+ },
13526
13544
  commandCaps: [
13527
13545
  "switch",
13528
13546
  "brightness",
13529
13547
  "color"
13530
13548
  ]
13549
+ }),
13550
+ actuator({
13551
+ id: "fan",
13552
+ 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.",
13553
+ deviceType: require_err_msg.DeviceType.Fan,
13554
+ outputCap: "switch",
13555
+ outputField: "on",
13556
+ member: {
13557
+ cap: "switch",
13558
+ fieldPath: "on"
13559
+ },
13560
+ commandCaps: ["switch", "fan-control"]
13561
+ }),
13562
+ actuator({
13563
+ id: "cover",
13564
+ 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.",
13565
+ deviceType: require_err_msg.DeviceType.Cover,
13566
+ outputCap: "group-active",
13567
+ outputField: "active",
13568
+ member: {
13569
+ cap: "cover",
13570
+ fieldPath: "state"
13571
+ },
13572
+ activeWhen: [
13573
+ "open",
13574
+ "opening",
13575
+ "stopped"
13576
+ ],
13577
+ commandCaps: ["cover"]
13578
+ }),
13579
+ actuator({
13580
+ id: "valve",
13581
+ 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.",
13582
+ deviceType: require_err_msg.DeviceType.Valve,
13583
+ outputCap: "group-active",
13584
+ outputField: "active",
13585
+ member: {
13586
+ cap: "valve",
13587
+ fieldPath: "state"
13588
+ },
13589
+ activeWhen: [
13590
+ "open",
13591
+ "opening",
13592
+ "stopped"
13593
+ ],
13594
+ commandCaps: ["valve"]
13595
+ }),
13596
+ actuator({
13597
+ id: "lock",
13598
+ 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.",
13599
+ deviceType: require_err_msg.DeviceType.Lock,
13600
+ outputCap: "group-active",
13601
+ outputField: "active",
13602
+ member: {
13603
+ cap: "lock-control",
13604
+ fieldPath: "state"
13605
+ },
13606
+ activeWhen: ["locked"],
13607
+ commandCaps: ["lock-control"]
13608
+ }),
13609
+ actuator({
13610
+ id: "media-player",
13611
+ 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.",
13612
+ deviceType: require_err_msg.DeviceType.MediaPlayer,
13613
+ outputCap: "group-active",
13614
+ outputField: "active",
13615
+ member: {
13616
+ cap: "media-player",
13617
+ fieldPath: "state"
13618
+ },
13619
+ activeWhen: ["playing", "buffering"],
13620
+ commandCaps: ["media-player"]
13621
+ }),
13622
+ actuator({
13623
+ id: "climate",
13624
+ 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.",
13625
+ deviceType: require_err_msg.DeviceType.Thermostat,
13626
+ memberDeviceTypes: [require_err_msg.DeviceType.Thermostat, require_err_msg.DeviceType.Climate],
13627
+ outputCap: "group-active",
13628
+ outputField: "active",
13629
+ member: {
13630
+ cap: "climate-control",
13631
+ fieldPath: "mode"
13632
+ },
13633
+ activeWhen: [
13634
+ "heat",
13635
+ "cool",
13636
+ "auto",
13637
+ "heat_cool",
13638
+ "fan_only",
13639
+ "dry"
13640
+ ],
13641
+ commandCaps: ["climate-control"]
13531
13642
  })
13532
13643
  ];
13533
13644
  function actuator(input) {
13534
13645
  return {
13535
- ...input,
13646
+ id: input.id,
13647
+ summary: input.summary,
13648
+ deviceType: input.deviceType,
13649
+ outputCap: input.outputCap,
13650
+ outputField: input.outputField,
13651
+ commandCaps: input.commandCaps,
13536
13652
  family: "actuator",
13537
13653
  role: void 0,
13538
13654
  memberDeviceType: input.deviceType,
13539
- outputField: "on",
13655
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
13656
+ activeWhen: input.activeWhen,
13540
13657
  quantity: "none",
13541
13658
  baseUnit: void 0,
13542
13659
  aggregates: require_composition.BINARY_AGGREGATES,
13543
13660
  defaultAggregate: "any",
13544
13661
  empty: "offline",
13545
- accept: [{
13546
- cap: "switch",
13547
- fieldPath: "on"
13548
- }],
13662
+ accept: [input.member],
13549
13663
  companions: [CHANGED]
13550
13664
  };
13551
13665
  }
@@ -13742,7 +13856,7 @@ async function runFanOut(calls, invoke) {
13742
13856
  //#region src/generated/history-catalog.ts
13743
13857
  /**
13744
13858
  * Cap history catalog, projected from every `*.cap.ts` `history` block.
13745
- * 42 caps with series (76 series), 32 caps answered `'none'`.
13859
+ * 42 caps with series (76 series), 33 caps answered `'none'`.
13746
13860
  * Field keys are `runtimeState` paths (`a.b` into fixed objects only).
13747
13861
  */
13748
13862
  var HISTORY_CATALOG = {
@@ -14148,6 +14262,7 @@ var HISTORY_NONE_CAPS = [
14148
14262
  "doorbell",
14149
14263
  "event-emitter",
14150
14264
  "feature-probe",
14265
+ "group-active",
14151
14266
  "group-commands",
14152
14267
  "group-status",
14153
14268
  "image",
@@ -14181,7 +14296,7 @@ function historyFieldsFor(capName) {
14181
14296
  * Project a statistics or integral helper onto a new device (D944 §3).
14182
14297
  * The value field is `series`: the composer fills it from history.
14183
14298
  */
14184
- var NOW = {
14299
+ var NOW$1 = {
14185
14300
  kind: "expression",
14186
14301
  expr: "now",
14187
14302
  bindings: {}
@@ -14205,6 +14320,20 @@ function counts(member) {
14205
14320
  total: series(member, "total", "offline")
14206
14321
  };
14207
14322
  }
14323
+ var PER_SYMBOL = {
14324
+ second: "s",
14325
+ minute: "min",
14326
+ hour: "h"
14327
+ };
14328
+ /**
14329
+ * The unit of a rate: the source series' unit per second, minute or hour
14330
+ * (`°C/h`). A source with no unit of its own (a generic number) has none to
14331
+ * borrow, so the label is null and the chart shows the bare number.
14332
+ */
14333
+ function derivativeUnitLabel(spec) {
14334
+ const unit = historyFieldsFor(spec.source.cap)[spec.source.fieldPath]?.unit;
14335
+ return unit === void 0 ? null : `${unit}/${PER_SYMBOL[spec.per]}`;
14336
+ }
14208
14337
  function declaredSeriesKind(member) {
14209
14338
  const field = historyFieldsFor(member.cap)[member.fieldPath];
14210
14339
  if (field === void 0) return null;
@@ -14227,7 +14356,7 @@ function draft(input) {
14227
14356
  cap: input.cap,
14228
14357
  fields: {
14229
14358
  [input.field]: series(input.member, "value", input.empty),
14230
- lastFetchedAt: NOW
14359
+ lastFetchedAt: NOW$1
14231
14360
  }
14232
14361
  }, {
14233
14362
  kind: "fields",
@@ -14255,6 +14384,29 @@ function statisticDraft(spec, retention = require_composition.DEFAULT_HISTORY_RE
14255
14384
  })
14256
14385
  };
14257
14386
  }
14387
+ /**
14388
+ * A derivative reads a gauge or a counter and publishes its rate per second,
14389
+ * minute or hour. The output is a plain number: a rate of a temperature is not
14390
+ * a temperature, so the chart carries no inherited unit.
14391
+ */
14392
+ function derivativeDraft(spec, retention = require_composition.DEFAULT_HISTORY_RETENTION) {
14393
+ const problems = [...historyProblems(spec.source)];
14394
+ const kind = declaredSeriesKind(spec.source);
14395
+ if (kind === "enum" || kind === "boolean") problems.push(problem("kind-mismatch", "helper.source", "a derivative reads a number that moves, not a yes/no or a state"));
14396
+ const tier = require_composition.planStatisticTier(spec.window, retention);
14397
+ if (!tier.ok) problems.push(problem("retention", "helper.window", tier.reason));
14398
+ return {
14399
+ problems,
14400
+ draft: draft({
14401
+ role: require_err_msg.DeviceRole.NumericSensor,
14402
+ cap: "numeric-sensor",
14403
+ field: "value",
14404
+ empty: "null",
14405
+ spec,
14406
+ member: spec.source
14407
+ })
14408
+ };
14409
+ }
14258
14410
  function integralDraft(spec, retention = require_composition.DEFAULT_HISTORY_RETENTION) {
14259
14411
  const problems = [...historyProblems(spec.source)];
14260
14412
  const kind = declaredSeriesKind(spec.source);
@@ -14278,6 +14430,163 @@ function integralDraft(spec, retention = require_composition.DEFAULT_HISTORY_RET
14278
14430
  };
14279
14431
  }
14280
14432
  //#endregion
14433
+ //#region src/composition/helpers/compile-math.ts
14434
+ /**
14435
+ * Math helpers projected onto a new sensor (D944 phase 3). A threshold is the
14436
+ * `schmitt` builtin, a difference and a ratio are small expressions, a sum is
14437
+ * the group reducer (so an unreporting input is left out, not read as 0), and
14438
+ * a free expression is the expression engine unchanged — it still includes a
14439
+ * stale input, and the template says so.
14440
+ */
14441
+ var NOW = {
14442
+ kind: "expression",
14443
+ expr: "now",
14444
+ bindings: {}
14445
+ };
14446
+ function from$1(member) {
14447
+ return {
14448
+ kind: "from",
14449
+ source: member.source,
14450
+ cap: member.cap,
14451
+ fieldPath: member.fieldPath
14452
+ };
14453
+ }
14454
+ function expression$1(expr, bindings) {
14455
+ return {
14456
+ kind: "expression",
14457
+ expr,
14458
+ bindings
14459
+ };
14460
+ }
14461
+ function sameMember$1(a, b) {
14462
+ return a.source.addonId === b.source.addonId && a.source.stableId === b.source.stableId && a.cap === b.cap && a.fieldPath === b.fieldPath;
14463
+ }
14464
+ function numericSensor(helper, value, extra = []) {
14465
+ return {
14466
+ target: {
14467
+ kind: "new",
14468
+ type: require_err_msg.DeviceType.Sensor,
14469
+ role: require_err_msg.DeviceRole.NumericSensor
14470
+ },
14471
+ helper,
14472
+ features: [{
14473
+ kind: "fields",
14474
+ cap: "numeric-sensor",
14475
+ fields: {
14476
+ value,
14477
+ lastFetchedAt: NOW
14478
+ }
14479
+ }, ...extra]
14480
+ };
14481
+ }
14482
+ function binarySensor(helper, on) {
14483
+ return {
14484
+ target: {
14485
+ kind: "new",
14486
+ type: require_err_msg.DeviceType.Sensor,
14487
+ role: require_err_msg.DeviceRole.BinarySensor
14488
+ },
14489
+ helper,
14490
+ features: [{
14491
+ kind: "fields",
14492
+ cap: "binary",
14493
+ fields: {
14494
+ on,
14495
+ lastChangedAt: NOW
14496
+ }
14497
+ }]
14498
+ };
14499
+ }
14500
+ function thresholdDraft(spec) {
14501
+ const problems = [];
14502
+ const high = signedToken(spec.high);
14503
+ const low = signedToken(spec.low);
14504
+ if (high === null) problems.push(problem("kind-mismatch", "helper.high", "the high edge is a finite number"));
14505
+ if (low === null) problems.push(problem("kind-mismatch", "helper.low", "the low edge is a finite number"));
14506
+ if (high !== null && low !== null && spec.low > spec.high) problems.push(problem("kind-mismatch", "helper.low", `the low edge (${spec.low}) is above the high edge (${spec.high}) — it turns on at the high edge and off at the low one`));
14507
+ const onFor = unsignedToken(spec.onForMs);
14508
+ const offFor = unsignedToken(spec.offForMs);
14509
+ if (onFor === null || offFor === null) problems.push(problem("kind-mismatch", "helper.onForMs", "the on and off delays are a non-negative number of milliseconds"));
14510
+ const delays = onFor !== null && offFor !== null && (spec.onForMs > 0 || spec.offForMs > 0) ? `, ${onFor}, ${offFor}` : "";
14511
+ return {
14512
+ problems,
14513
+ draft: binarySensor(spec, expression$1(`coalesce(schmitt(x, ${high ?? "0"}, ${low ?? "0"}${delays}), false)`, { x: from$1(spec.source) }))
14514
+ };
14515
+ }
14516
+ var NAMES = ["a", "b"];
14517
+ function arithmeticProblems(spec) {
14518
+ const problems = [];
14519
+ const exactlyTwo = spec.op !== "sum";
14520
+ if (exactlyTwo && spec.inputs.length !== 2) problems.push(problem("member-constraint", "helper.inputs", `a ${spec.op} takes exactly two readings, the first ${spec.op === "ratio" ? "over" : "minus"} the second`));
14521
+ if (!exactlyTwo && spec.inputs.length < 2) problems.push(problem("member-constraint", "helper.inputs", "a sum takes two or more readings"));
14522
+ spec.inputs.forEach((input, index) => {
14523
+ if (spec.inputs.slice(0, index).some((other) => sameMember$1(other, input))) problems.push(problem("member-constraint", `helper.inputs[${index}]`, "this reading is already an input"));
14524
+ });
14525
+ return problems;
14526
+ }
14527
+ var GROUP_COUNTS = [
14528
+ "included",
14529
+ "excluded",
14530
+ "total"
14531
+ ];
14532
+ function arithmeticDraft(spec) {
14533
+ const problems = arithmeticProblems(spec);
14534
+ if (spec.op === "sum") {
14535
+ const reads = spec.inputs.map((input) => ({
14536
+ source: input.source,
14537
+ cap: input.cap,
14538
+ fieldPath: input.fieldPath
14539
+ }));
14540
+ const aggregate = (publish) => ({
14541
+ kind: "aggregate",
14542
+ reducer: "sum",
14543
+ reads,
14544
+ publish,
14545
+ empty: "null"
14546
+ });
14547
+ const counts = Object.fromEntries(GROUP_COUNTS.map((name) => [name, aggregate(name)]));
14548
+ return {
14549
+ problems,
14550
+ draft: numericSensor(spec, aggregate("value"), [{
14551
+ kind: "fields",
14552
+ cap: "group-status",
14553
+ fields: counts
14554
+ }])
14555
+ };
14556
+ }
14557
+ const bindings = Object.fromEntries(spec.inputs.slice(0, 2).map((input, index) => [NAMES[index] ?? "a", from$1(input)]));
14558
+ return {
14559
+ problems,
14560
+ draft: numericSensor(spec, expression$1(spec.op === "difference" ? "a - b" : "b == 0 ? null : a / b", bindings))
14561
+ };
14562
+ }
14563
+ function expressionProblems(spec) {
14564
+ const problems = [];
14565
+ const seen = /* @__PURE__ */ new Set();
14566
+ spec.inputs.forEach((input, index) => {
14567
+ const at = `helper.inputs[${index}].name`;
14568
+ if (!isIdentifier(input.name)) problems.push(problem("expression-invalid", at, `\`${input.name}\` is not a name — use letters, digits and _, starting with a letter`));
14569
+ if (seen.has(input.name)) problems.push(problem("expression-invalid", at, `the name \`${input.name}\` is used twice`));
14570
+ seen.add(input.name);
14571
+ });
14572
+ if (problems.length > 0) return problems;
14573
+ const bindings = Object.fromEntries(spec.inputs.map((input) => [input.name, from$1(input.member)]));
14574
+ const message = require_composition.validateExpressionSource({
14575
+ expr: spec.expr,
14576
+ bindings
14577
+ });
14578
+ return message === null ? [] : [problem("expression-invalid", "helper.expr", message)];
14579
+ }
14580
+ function expressionDraft(spec) {
14581
+ const problems = expressionProblems(spec);
14582
+ const bindings = Object.fromEntries(spec.inputs.map((input) => [input.name, from$1(input.member)]));
14583
+ const source = expression$1(spec.expr, bindings);
14584
+ return {
14585
+ problems,
14586
+ draft: spec.output === "boolean" ? binarySensor(spec, source) : numericSensor(spec, source)
14587
+ };
14588
+ }
14589
+ //#endregion
14281
14590
  //#region src/composition/helpers/compile.ts
14282
14591
  function sameSource(a, b) {
14283
14592
  return a.addonId === b.addonId && a.stableId === b.stableId;
@@ -14306,7 +14615,8 @@ function aggregateSource(template, spec, reads, publish) {
14306
14615
  reducer: spec.aggregate,
14307
14616
  reads: [...reads],
14308
14617
  publish,
14309
- empty: template.empty
14618
+ empty: template.empty,
14619
+ ...template.activeWhen === void 0 ? {} : { activeWhen: [...template.activeWhen] }
14310
14620
  };
14311
14621
  }
14312
14622
  function draftOf$1(template, spec) {
@@ -14441,8 +14751,26 @@ function compileHelper(spec, retention = require_composition.DEFAULT_HISTORY_RET
14441
14751
  };
14442
14752
  return parseDraft(built.draft);
14443
14753
  }
14754
+ case "derivative": {
14755
+ const built = derivativeDraft(spec, retention);
14756
+ if (built.problems.length > 0) return {
14757
+ ok: false,
14758
+ problems: built.problems
14759
+ };
14760
+ return parseDraft(built.draft);
14761
+ }
14762
+ case "threshold": return compileMath(thresholdDraft(spec));
14763
+ case "arithmetic": return compileMath(arithmeticDraft(spec));
14764
+ case "expression": return compileMath(expressionDraft(spec));
14444
14765
  }
14445
14766
  }
14767
+ function compileMath(built) {
14768
+ if (built.problems.length > 0) return {
14769
+ ok: false,
14770
+ problems: built.problems
14771
+ };
14772
+ return parseDraft(built.draft);
14773
+ }
14446
14774
  function compileGroup(spec) {
14447
14775
  const template = groupTemplate(spec.templateId);
14448
14776
  if (template === null) return {
@@ -14505,8 +14833,8 @@ function deviceTypeProblems(spec, facts) {
14505
14833
  if (template === null) return [];
14506
14834
  return spec.members.flatMap((member, index) => {
14507
14835
  const deviceType = facts.get(require_composition.compositionSourceKey(member.source))?.deviceType;
14508
- if (deviceType === void 0 || deviceType === template.memberDeviceType) return [];
14509
- return [problem("member-constraint", `helper.members[${index}]`, `a ${template.id} group takes ${template.memberDeviceType} devices; this member is a ${deviceType}`)];
14836
+ if (deviceType === void 0 || template.memberDeviceTypes.includes(deviceType)) return [];
14837
+ return [problem("member-constraint", `helper.members[${index}]`, `a ${template.id} group takes ${template.memberDeviceTypes.join(" or ")} devices; this member is a ${deviceType}`)];
14510
14838
  });
14511
14839
  }
14512
14840
  /**
@@ -14520,6 +14848,7 @@ function helperSaveProblems(composition, facts) {
14520
14848
  if (!compiled.ok) return compiled.problems;
14521
14849
  if (!sameHelperBody(composition, compiled.composition)) return [problem("helper-mismatch", "helper", "the stored features are not this template — the helper spec is the only writer")];
14522
14850
  if (spec.kind === "appliance") return applianceMemberProblems(spec.member, facts);
14851
+ if (spec.kind === "arithmetic") return arithmeticQuantityProblems(spec, facts);
14523
14852
  if (spec.kind !== "group") return [];
14524
14853
  return [...quantityProblems(spec, facts), ...deviceTypeProblems(spec, facts)];
14525
14854
  }
@@ -14531,6 +14860,21 @@ function applianceMemberProblems(member, facts) {
14531
14860
  if (fact.quantity !== void 0 && fact.quantity !== "power") problems.push(problem("mixed-quantity", "helper.member", `an appliance helper reads power (W); this member is ${fact.quantity}`));
14532
14861
  return problems;
14533
14862
  }
14863
+ /**
14864
+ * A sum, a difference and a ratio combine readings of ONE quantity. The ratio
14865
+ * of two powers is a plain number, but a power over an energy is not a ratio of
14866
+ * anything, so it is refused too. Inputs that did not report a quantity are not
14867
+ * guessed at.
14868
+ */
14869
+ function arithmeticQuantityProblems(spec, facts) {
14870
+ const known = spec.inputs.flatMap((input) => {
14871
+ const quantity = facts.get(require_composition.compositionSourceKey(input.source))?.quantity;
14872
+ return quantity === void 0 ? [] : [quantity];
14873
+ });
14874
+ const distinct = [...new Set(known)];
14875
+ if (distinct.length <= 1) return [];
14876
+ return [problem("mixed-quantity", "helper.inputs", `a ${spec.op} needs readings of one quantity; these inputs report ${distinct.join(" and ")}`)];
14877
+ }
14534
14878
  //#endregion
14535
14879
  //#region src/composition/composition-graph.ts
14536
14880
  /**
@@ -47040,7 +47384,20 @@ var NcRuleInputSchema = zod.z.object({
47040
47384
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47041
47385
  * path does not run Zod defaults.
47042
47386
  */
47043
- groupingSec: zod.z.number().int().min(5).max(60).optional()
47387
+ groupingSec: zod.z.number().int().min(5).max(60).optional(),
47388
+ /**
47389
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47390
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47391
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47392
+ * same mark the Library writes, and records whose notification it was and
47393
+ * when. A channel with no buttons gets the same two choices as links.
47394
+ *
47395
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47396
+ * cap path): absent and `false` both mean off, which is what every rule
47397
+ * authored before this field meant. An editor turns it off by writing
47398
+ * `false`, so it needs no `clear` key.
47399
+ */
47400
+ askVerdict: zod.z.boolean().optional()
47044
47401
  });
47045
47402
  /**
47046
47403
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -65684,7 +66041,17 @@ var groupCommandsCapability = {
65684
66041
  scope: "device",
65685
66042
  deviceNative: true,
65686
66043
  mode: "singleton",
65687
- deviceTypes: [require_err_msg.DeviceType.Switch, require_err_msg.DeviceType.Light],
66044
+ deviceTypes: [
66045
+ require_err_msg.DeviceType.Switch,
66046
+ require_err_msg.DeviceType.Light,
66047
+ require_err_msg.DeviceType.Fan,
66048
+ require_err_msg.DeviceType.Cover,
66049
+ require_err_msg.DeviceType.Valve,
66050
+ require_err_msg.DeviceType.Lock,
66051
+ require_err_msg.DeviceType.MediaPlayer,
66052
+ require_err_msg.DeviceType.Thermostat,
66053
+ require_err_msg.DeviceType.Climate
66054
+ ],
65688
66055
  methods: {},
65689
66056
  status: {
65690
66057
  schema: GroupCommandsSchema,
@@ -65702,6 +66069,51 @@ var groupCommandsCapability = {
65702
66069
  history: "none"
65703
66070
  };
65704
66071
  //#endregion
66072
+ //#region src/capabilities/group-active.cap.ts
66073
+ /**
66074
+ * Whether a helper actuator group counts as active (D944): at least one
66075
+ * included member is, or every included member is. What "active" means is the
66076
+ * type's: a cover is open, a lock is locked, a media player is playing, a
66077
+ * climate device is not off. A group of lights, switches and fans reports
66078
+ * `switch.on` instead; this cap is for the types whose own state is not a
66079
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
66080
+ * empty included set takes the device offline, never `active: false`.
66081
+ */
66082
+ var GroupActiveSchema = zod.z.object({
66083
+ active: zod.z.boolean().meta({ label: "Active" }),
66084
+ /** Ms epoch when the slice was last updated. */
66085
+ lastChangedAt: zod.z.number()
66086
+ });
66087
+ var groupActiveCapability = {
66088
+ name: "group-active",
66089
+ scope: "device",
66090
+ deviceNative: true,
66091
+ mode: "singleton",
66092
+ deviceTypes: [
66093
+ require_err_msg.DeviceType.Cover,
66094
+ require_err_msg.DeviceType.Valve,
66095
+ require_err_msg.DeviceType.Lock,
66096
+ require_err_msg.DeviceType.MediaPlayer,
66097
+ require_err_msg.DeviceType.Climate,
66098
+ require_err_msg.DeviceType.Thermostat
66099
+ ],
66100
+ methods: {},
66101
+ status: {
66102
+ schema: GroupActiveSchema,
66103
+ kind: "push"
66104
+ },
66105
+ runtimeState: GroupActiveSchema,
66106
+ /**
66107
+ * Runtime-state durability: **session** — recomputed from the members on the
66108
+ * next source event; a restored value would outlive the members it named.
66109
+ *
66110
+ * See `RuntimeStateDurability`. Enforced by
66111
+ * `scripts/check-runtime-state-durability.ts`.
66112
+ */
66113
+ durability: "session",
66114
+ history: "none"
66115
+ };
66116
+ //#endregion
65705
66117
  //#region src/capabilities/group-status.cap.ts
65706
66118
  /**
65707
66119
  * How many members of a helper group answered (D944). Counts are integers,
@@ -65734,7 +66146,14 @@ var groupStatusCapability = {
65734
66146
  deviceTypes: [
65735
66147
  require_err_msg.DeviceType.Sensor,
65736
66148
  require_err_msg.DeviceType.Switch,
65737
- require_err_msg.DeviceType.Light
66149
+ require_err_msg.DeviceType.Light,
66150
+ require_err_msg.DeviceType.Fan,
66151
+ require_err_msg.DeviceType.Cover,
66152
+ require_err_msg.DeviceType.Valve,
66153
+ require_err_msg.DeviceType.Lock,
66154
+ require_err_msg.DeviceType.MediaPlayer,
66155
+ require_err_msg.DeviceType.Thermostat,
66156
+ require_err_msg.DeviceType.Climate
65738
66157
  ],
65739
66158
  methods: {},
65740
66159
  status: {
@@ -67236,7 +67655,14 @@ var lockControlCapability = {
67236
67655
  }),
67237
67656
  unlock: require_sleep.method(zod.z.object({
67238
67657
  deviceId: zod.z.number().int().nonnegative(),
67239
- code: zod.z.string().min(1).optional()
67658
+ code: zod.z.string().min(1).optional(),
67659
+ /**
67660
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
67661
+ * with one call is refused unless the caller says it was confirmed. A
67662
+ * single lock ignores it. The refusal lives in the callee, so no
67663
+ * automation or API caller reaches the doors by skipping a dialog.
67664
+ */
67665
+ confirm: zod.z.boolean().optional()
67240
67666
  }), zod.z.void(), {
67241
67667
  kind: "mutation",
67242
67668
  auth: "protected"
@@ -75768,7 +76194,8 @@ var switchCapability = {
75768
76194
  deviceTypes: [
75769
76195
  require_err_msg.DeviceType.Switch,
75770
76196
  require_err_msg.DeviceType.Siren,
75771
- require_err_msg.DeviceType.Light
76197
+ require_err_msg.DeviceType.Light,
76198
+ require_err_msg.DeviceType.Fan
75772
76199
  ],
75773
76200
  methods: { setState: require_sleep.method(zod.z.object({
75774
76201
  deviceId: zod.z.number(),
@@ -78653,13 +79080,116 @@ function createDeviceExtensionProvider(options) {
78653
79080
  };
78654
79081
  }
78655
79082
  //#endregion
79083
+ //#region src/composition/helpers/actuator-methods.ts
79084
+ /**
79085
+ * One method table per actuator type (D944). A group offers a method only
79086
+ * when every member can take it, so a command never reaches a member that
79087
+ * lacks the surface: the cap must be bound by every member (the primary cap of
79088
+ * the type is the exception — a member that lacks it is simply not commanded),
79089
+ * and a row that names a device feature needs every member to advertise it.
79090
+ * Offline does not remove a method: capability is the binding, not liveness.
79091
+ */
79092
+ var ACTUATOR_TEMPLATE_IDS = [
79093
+ "switch",
79094
+ "light",
79095
+ "fan",
79096
+ "cover",
79097
+ "valve",
79098
+ "lock",
79099
+ "media-player",
79100
+ "climate"
79101
+ ];
79102
+ function row$1(cap, method, requires = null, confirm = false) {
79103
+ return {
79104
+ cap,
79105
+ method,
79106
+ requires,
79107
+ confirm
79108
+ };
79109
+ }
79110
+ /** The first row's cap is the type's own: always offered, members without it are skipped. */
79111
+ var ACTUATOR_METHODS = {
79112
+ switch: [row$1("switch", "setState")],
79113
+ light: [
79114
+ row$1("switch", "setState"),
79115
+ row$1("brightness", "setBrightness"),
79116
+ row$1("color", "setColor")
79117
+ ],
79118
+ fan: [
79119
+ row$1("switch", "setState"),
79120
+ row$1("fan-control", "setPercentage", require_err_msg.DeviceFeature.FanSpeed),
79121
+ row$1("fan-control", "setPreset", require_err_msg.DeviceFeature.FanPreset),
79122
+ row$1("fan-control", "setDirection", require_err_msg.DeviceFeature.FanDirection),
79123
+ row$1("fan-control", "setOscillating", require_err_msg.DeviceFeature.FanOscillating)
79124
+ ],
79125
+ cover: [
79126
+ row$1("cover", "open"),
79127
+ row$1("cover", "close"),
79128
+ row$1("cover", "stop"),
79129
+ row$1("cover", "setPosition", require_err_msg.DeviceFeature.CoverPositionable),
79130
+ row$1("cover", "setTiltPosition", require_err_msg.DeviceFeature.CoverTilt)
79131
+ ],
79132
+ valve: [
79133
+ row$1("valve", "open"),
79134
+ row$1("valve", "close"),
79135
+ row$1("valve", "setPosition", require_err_msg.DeviceFeature.ValvePositionable)
79136
+ ],
79137
+ lock: [row$1("lock-control", "lock"), row$1("lock-control", "unlock", null, true)],
79138
+ "media-player": [
79139
+ row$1("media-player", "play"),
79140
+ row$1("media-player", "pause"),
79141
+ row$1("media-player", "setVolume", require_err_msg.DeviceFeature.MediaPlayerVolume)
79142
+ ],
79143
+ climate: [
79144
+ row$1("climate-control", "setMode"),
79145
+ row$1("climate-control", "setTarget"),
79146
+ row$1("climate-control", "setFanMode", require_err_msg.DeviceFeature.ClimateFanMode)
79147
+ ]
79148
+ };
79149
+ function actuatorMethodRow(templateId, cap, method) {
79150
+ return ACTUATOR_METHODS[templateId].find((item) => item.cap === cap && item.method === method) ?? null;
79151
+ }
79152
+ /**
79153
+ * The rows every member can take. An empty member list shares nothing; the
79154
+ * caller passes a surface for every member it could read, or none at all.
79155
+ */
79156
+ function sharedActuatorMethods(templateId, members) {
79157
+ if (members.length === 0) return [];
79158
+ const rows = ACTUATOR_METHODS[templateId];
79159
+ const primary = rows[0]?.cap;
79160
+ return rows.filter((item) => {
79161
+ if (!(item.cap === primary || members.every((member) => member.caps.includes(item.cap)))) return false;
79162
+ const needed = item.requires;
79163
+ return needed === null || members.every((member) => member.features.includes(needed));
79164
+ });
79165
+ }
79166
+ /** The device features every member has, among those the table gates on, in table order. */
79167
+ function sharedActuatorFeatures(templateId, featureLists) {
79168
+ if (featureLists.length === 0) return [];
79169
+ const gated = ACTUATOR_METHODS[templateId].flatMap((item) => item.requires === null ? [] : [item.requires]);
79170
+ return [...new Set(gated)].filter((feature) => featureLists.every((features) => features.includes(feature)));
79171
+ }
79172
+ var UNLOCK_CONFIRMATION = "unlocking a group of locks needs an explicit confirmation (confirm: true) — it was not given, so no lock was sent anything";
79173
+ /**
79174
+ * Why a call must not be fanned out, or null. Only a confirmed row passes
79175
+ * without `confirm: true`; anything else is the caller's to refuse by name.
79176
+ */
79177
+ function methodRefusal(method, args) {
79178
+ if (method === null) return "that method is not one this group shares";
79179
+ if (method.confirm && args.confirm !== true) return UNLOCK_CONFIRMATION;
79180
+ return null;
79181
+ }
79182
+ //#endregion
78656
79183
  //#region src/composition/helpers/aggregate.ts
78657
79184
  function isBinaryReducer(reducer) {
78658
79185
  return reducer === "any" || reducer === "all";
78659
79186
  }
78660
- function includedValue(reducer, member) {
79187
+ function includedValue(reducer, member, activeWhen) {
78661
79188
  if (member.state !== "fresh") return null;
78662
- if (isBinaryReducer(reducer)) return typeof member.value === "boolean" ? member.value : null;
79189
+ if (isBinaryReducer(reducer)) {
79190
+ if (activeWhen !== void 0) return typeof member.value === "string" ? activeWhen.includes(member.value) : null;
79191
+ return typeof member.value === "boolean" ? member.value : null;
79192
+ }
78663
79193
  return typeof member.value === "number" && Number.isFinite(member.value) ? member.value : null;
78664
79194
  }
78665
79195
  function median$1(values) {
@@ -78709,7 +79239,7 @@ function emptyOutcome(empty, counts) {
78709
79239
  /** Pure. `members` is not mutated. An empty included set is null or unavailable, never 0 or false. */
78710
79240
  function evaluateAggregate(input) {
78711
79241
  const included = input.members.flatMap((member) => {
78712
- const value = includedValue(input.reducer, member);
79242
+ const value = includedValue(input.reducer, member, input.activeWhen);
78713
79243
  return value === null ? [] : [value];
78714
79244
  });
78715
79245
  const counts = countsOf(included.length, input.members.length);
@@ -78755,12 +79285,12 @@ function evaluateAggregate(input) {
78755
79285
  //#region src/composition/helpers/source-pick.ts
78756
79286
  /** What the member step asks the selector for this template. */
78757
79287
  function sourcePickForTemplate(template) {
78758
- const valueKind = template.family === "numeric" ? "number" : "boolean";
79288
+ const valueKind = template.family === "numeric" ? "number" : template.activeWhen === void 0 ? "boolean" : "state";
78759
79289
  return {
78760
79290
  purpose: "member",
78761
79291
  multiple: true,
78762
79292
  max: 64,
78763
- deviceTypes: [template.memberDeviceType],
79293
+ deviceTypes: [...template.memberDeviceTypes],
78764
79294
  accept: template.accept.map((field) => ({
78765
79295
  ...field,
78766
79296
  valueKind
@@ -78769,7 +79299,7 @@ function sourcePickForTemplate(template) {
78769
79299
  required: false,
78770
79300
  minRetentionMs: null
78771
79301
  },
78772
- mustBindCaps: template.family === "actuator" ? [template.outputCap] : [],
79302
+ mustBindCaps: template.family === "actuator" ? template.accept.map((field) => field.cap) : [],
78773
79303
  exclude: []
78774
79304
  };
78775
79305
  }
@@ -78802,6 +79332,35 @@ function historyAccept(kind) {
78802
79332
  }
78803
79333
  return fields;
78804
79334
  }
79335
+ /**
79336
+ * What the input step asks the selector for a math helper. A threshold reads
79337
+ * one number, a difference or a ratio two, a sum and an expression several.
79338
+ * A derivative reads one historised number.
79339
+ */
79340
+ function sourcePickForMath(kind) {
79341
+ if (kind === "derivative") return {
79342
+ ...sourcePickForHistory("statistic"),
79343
+ accept: numericAccept()
79344
+ };
79345
+ const multiple = kind === "sum" || kind === "expression";
79346
+ const max = kind === "difference" || kind === "ratio" ? 2 : multiple ? 64 : 1;
79347
+ return {
79348
+ purpose: "reading",
79349
+ multiple: multiple || max > 1,
79350
+ max,
79351
+ deviceTypes: [],
79352
+ accept: numericAccept(),
79353
+ history: {
79354
+ required: false,
79355
+ minRetentionMs: null
79356
+ },
79357
+ mustBindCaps: [],
79358
+ exclude: []
79359
+ };
79360
+ }
79361
+ function numericAccept() {
79362
+ return historyAccept("statistic").filter((field) => field.valueKind === "number");
79363
+ }
78805
79364
  //#endregion
78806
79365
  //#region src/units/unit-table.ts
78807
79366
  /**
@@ -79404,6 +79963,7 @@ function aggregateRead(reading) {
79404
79963
  function aggregateField(source, read) {
79405
79964
  const outcome = evaluateAggregate({
79406
79965
  reducer: source.reducer,
79966
+ ...source.activeWhen === void 0 ? {} : { activeWhen: source.activeWhen },
79407
79967
  members: source.reads.map((item) => aggregateRead(read(item))),
79408
79968
  publish: source.publish,
79409
79969
  empty: source.empty
@@ -82198,6 +82758,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
82198
82758
  featureProbe: featureProbeCapability,
82199
82759
  flood: floodCapability,
82200
82760
  gas: gasCapability,
82761
+ groupActive: groupActiveCapability,
82201
82762
  groupCommands: groupCommandsCapability,
82202
82763
  groupStatus: groupStatusCapability,
82203
82764
  heatingValve: heatingValveCapability,
@@ -87537,6 +88098,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
87537
88098
  access: "view",
87538
88099
  role: "viewer"
87539
88100
  },
88101
+ "groupActive.getStatus": {
88102
+ capName: "group-active",
88103
+ capScope: "device",
88104
+ addonId: null,
88105
+ access: "view",
88106
+ role: "viewer"
88107
+ },
87540
88108
  "groupCommands.getStatus": {
87541
88109
  capName: "group-commands",
87542
88110
  capScope: "device",
@@ -94096,6 +94664,7 @@ var KNOWN_CAP_NAMES = [
94096
94664
  "filesystem-browse",
94097
94665
  "flood",
94098
94666
  "gas",
94667
+ "group-active",
94099
94668
  "group-commands",
94100
94669
  "group-status",
94101
94670
  "heating-valve",
@@ -94247,6 +94816,7 @@ var DEVICE_CAP_NAMES = [
94247
94816
  "feature-probe",
94248
94817
  "flood",
94249
94818
  "gas",
94819
+ "group-active",
94250
94820
  "group-commands",
94251
94821
  "group-status",
94252
94822
  "heating-valve",
@@ -97799,6 +98369,10 @@ var SENSOR_CAP_VOCABULARY = {
97799
98369
  icon: "flame"
97800
98370
  }]
97801
98371
  },
98372
+ "group-active": {
98373
+ scope: "device",
98374
+ noun: t("Group state", "Stato del gruppo")
98375
+ },
97802
98376
  "group-commands": {
97803
98377
  scope: "device",
97804
98378
  noun: t("Group command", "Comando di gruppo")
@@ -100053,6 +100627,7 @@ var CAP_NAMES_WITH_STATUS = [
100053
100627
  "feature-probe",
100054
100628
  "flood",
100055
100629
  "gas",
100630
+ "group-active",
100056
100631
  "group-commands",
100057
100632
  "group-status",
100058
100633
  "heating-valve",
@@ -100178,6 +100753,7 @@ var CAPABILITY_NAMES = {
100178
100753
  filesystemBrowse: "filesystem-browse",
100179
100754
  flood: "flood",
100180
100755
  gas: "gas",
100756
+ groupActive: "group-active",
100181
100757
  groupCommands: "group-commands",
100182
100758
  groupStatus: "group-status",
100183
100759
  heatingValve: "heating-valve",
@@ -100587,6 +101163,10 @@ var CAPABILITY_ROUTER_KEYS = [
100587
101163
  key: "gas",
100588
101164
  name: "gas"
100589
101165
  },
101166
+ {
101167
+ key: "groupActive",
101168
+ name: "group-active"
101169
+ },
100590
101170
  {
100591
101171
  key: "groupCommands",
100592
101172
  name: "group-commands"
@@ -101112,6 +101692,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
101112
101692
  filesystemBrowseCapability,
101113
101693
  floodCapability,
101114
101694
  gasCapability,
101695
+ groupActiveCapability,
101115
101696
  groupCommandsCapability,
101116
101697
  groupStatusCapability,
101117
101698
  heatingValveCapability,
@@ -102191,6 +102772,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
102191
102772
  form: "single",
102192
102773
  optional: false
102193
102774
  }],
102775
+ "groupActive.getStatus": [{
102776
+ name: "deviceId",
102777
+ form: "single",
102778
+ optional: false
102779
+ }],
102194
102780
  "groupCommands.getStatus": [{
102195
102781
  name: "deviceId",
102196
102782
  form: "single",
@@ -104217,7 +104803,7 @@ function getCapsByProviderKind(kind) {
104217
104803
  //#region src/generated/runtime-state-durability.ts
104218
104804
  /**
104219
104805
  * Per-cap runtime-state policy, projected from every `*.cap.ts` that declares
104220
- * `runtimeState`. 75 caps: 39 `restored`, 36 `session`.
104806
+ * `runtimeState`. 76 caps: 39 `restored`, 37 `session`.
104221
104807
  *
104222
104808
  * A cap absent from this map has no runtime-state slice at all. A cap PRESENT
104223
104809
  * with `durability: 'session'` has one and has decided not to keep it.
@@ -104335,6 +104921,10 @@ var RUNTIME_STATE_POLICY = {
104335
104921
  durability: "restored",
104336
104922
  volatileFields: ["lastChangedAt"]
104337
104923
  },
104924
+ "group-active": {
104925
+ durability: "session",
104926
+ volatileFields: []
104927
+ },
104338
104928
  "group-commands": {
104339
104929
  durability: "session",
104340
104930
  volatileFields: []
@@ -115937,6 +116527,8 @@ function armRemovalReconcile(opts) {
115937
116527
  exports.ACCESSORY_LABEL = ACCESSORY_LABEL;
115938
116528
  exports.ACCESS_ROLES = ACCESS_ROLES;
115939
116529
  exports.ACTIVE_CRITERIA = require_composition.ACTIVE_CRITERIA;
116530
+ exports.ACTUATOR_METHODS = ACTUATOR_METHODS;
116531
+ exports.ACTUATOR_TEMPLATE_IDS = ACTUATOR_TEMPLATE_IDS;
115940
116532
  exports.ADMIN_SCOPE = ADMIN_SCOPE;
115941
116533
  exports.AGENT_MDNS_QUERY_NAME = AGENT_MDNS_QUERY_NAME;
115942
116534
  exports.AGENT_MDNS_SERVICE_TYPE = AGENT_MDNS_SERVICE_TYPE;
@@ -115963,6 +116555,7 @@ exports.APP_FLOW_STATE_PREFIX = APP_FLOW_STATE_PREFIX;
115963
116555
  exports.APP_REFUSED_PATH = APP_REFUSED_PATH;
115964
116556
  exports.APP_START_REFUSAL_MESSAGES = APP_START_REFUSAL_MESSAGES;
115965
116557
  exports.ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT = ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT;
116558
+ exports.ARITHMETIC_OPS = require_composition.ARITHMETIC_OPS;
115966
116559
  exports.AUDIO_ANALYSIS_CAP_NAME = AUDIO_ANALYSIS_CAP_NAME;
115967
116560
  exports.AUDIO_BACKEND_CHOICES = AUDIO_BACKEND_CHOICES;
115968
116561
  exports.AUDIO_CHUNK_FORMATS = require_sleep.AUDIO_CHUNK_FORMATS;
@@ -116024,6 +116617,7 @@ exports.ArchiveManifestSchema = ArchiveManifestSchema;
116024
116617
  exports.ArchiveSqliteSnapshotSchema = ArchiveSqliteSnapshotSchema;
116025
116618
  exports.ArchivedDebugNoteQueryInputSchema = ArchivedDebugNoteQueryInputSchema;
116026
116619
  exports.ArchivedDebugNoteSchema = ArchivedDebugNoteSchema;
116620
+ exports.ArithmeticHelperSpecSchema = require_composition.ArithmeticHelperSpecSchema;
116027
116621
  exports.AttachmentMediaTypeSchema = AttachmentMediaTypeSchema;
116028
116622
  exports.AttachmentSchema = AttachmentSchema;
116029
116623
  exports.AudioAnalysisResultSchema = AudioAnalysisResultSchema;
@@ -116404,6 +116998,7 @@ exports.DEFAULT_TIMELAPSE_PREVIEW_TEXT = DEFAULT_TIMELAPSE_PREVIEW_TEXT;
116404
116998
  exports.DEFAULT_TOKEN_EXPIRY = require_sleep.DEFAULT_TOKEN_EXPIRY;
116405
116999
  exports.DEFAULT_VIEWER_SCOPE = DEFAULT_VIEWER_SCOPE;
116406
117000
  exports.DEFINITIVE_DEVICE_REFUSALS = DEFINITIVE_DEVICE_REFUSALS;
117001
+ exports.DERIVATIVE_UNITS = require_composition.DERIVATIVE_UNITS;
116407
117002
  exports.DERIVED_CAMERA_SILENCED_CAP_NAMES = DERIVED_CAMERA_SILENCED_CAP_NAMES;
116408
117003
  exports.DETAIL_CROP_PADDING_FIELD = DETAIL_CROP_PADDING_FIELD;
116409
117004
  exports.DETAIL_CROP_PADDING_KEY = DETAIL_CROP_PADDING_KEY;
@@ -116463,6 +117058,7 @@ exports.DeclareKindResultSchema = DeclareKindResultSchema;
116463
117058
  exports.DeclaredDevices = DeclaredDevices;
116464
117059
  exports.DecodedAudioChunkSchema = require_sleep.DecodedAudioChunkSchema;
116465
117060
  exports.DeleteIntegrationResultSchema = DeleteIntegrationResultSchema;
117061
+ exports.DerivativeHelperSpecSchema = require_composition.DerivativeHelperSpecSchema;
116466
117062
  exports.DetailCropConventionSchema = DetailCropConventionSchema;
116467
117063
  exports.DetectionCatalogClassMapSchema = DetectionCatalogClassMapSchema;
116468
117064
  exports.DetectionSourceSchema = DetectionSourceSchema;
@@ -116549,6 +117145,7 @@ exports.EXPRESSION_BUILTIN_NAMES = require_composition.EXPRESSION_BUILTIN_NAMES;
116549
117145
  exports.EXPRESSION_COMPILE_CACHE_CAPACITY = require_composition.EXPRESSION_COMPILE_CACHE_CAPACITY;
116550
117146
  exports.EXPRESSION_IDENTIFIER_RE = require_composition.EXPRESSION_IDENTIFIER_RE;
116551
117147
  exports.EXPRESSION_INJECTED_NOW = require_composition.EXPRESSION_INJECTED_NOW;
117148
+ exports.EXPRESSION_OUTPUTS = require_composition.EXPRESSION_OUTPUTS;
116552
117149
  exports.EXTENSION_HOST_DEVICE_TYPES = EXTENSION_HOST_DEVICE_TYPES;
116553
117150
  exports.EXTENSION_SLOTS = EXTENSION_SLOTS;
116554
117151
  exports.EXTENSION_SLOT_IDS = EXTENSION_SLOT_IDS;
@@ -116615,6 +117212,7 @@ exports.ExpressionBindingSourceSchema = ExpressionBindingSourceSchema;
116615
117212
  exports.ExpressionEvalError = require_composition.ExpressionEvalError;
116616
117213
  exports.ExpressionFieldBindingSchema = ExpressionFieldBindingSchema;
116617
117214
  exports.ExpressionGlobalBindingSchema = ExpressionGlobalBindingSchema;
117215
+ exports.ExpressionHelperSpecSchema = require_composition.ExpressionHelperSpecSchema;
116618
117216
  exports.ExpressionLiteralBindingSchema = ExpressionLiteralBindingSchema;
116619
117217
  exports.ExpressionParseError = require_composition.ExpressionParseError;
116620
117218
  exports.ExpressionSourceSchema = ExpressionSourceSchema;
@@ -116671,6 +117269,7 @@ exports.GetStreamWithCodecInputSchema = GetStreamWithCodecInputSchema;
116671
117269
  exports.GlobalMetricsSchema = GlobalMetricsSchema;
116672
117270
  exports.GridLayoutPatchSchema = GridLayoutPatchSchema;
116673
117271
  exports.GridLayoutViewSchema = GridLayoutViewSchema;
117272
+ exports.GroupActiveSchema = GroupActiveSchema;
116674
117273
  exports.GroupCommandResultSchema = GroupCommandResultSchema;
116675
117274
  exports.GroupCommandsSchema = GroupCommandsSchema;
116676
117275
  exports.GroupHelperSpecSchema = require_composition.GroupHelperSpecSchema;
@@ -117824,6 +118423,7 @@ exports.TerminalSessionInfoSchema = TerminalSessionInfoSchema;
117824
118423
  exports.TestConnectionResultSchema = TestConnectionResultSchema$1;
117825
118424
  exports.TestConnectionStatusEnum = TestConnectionStatusEnum;
117826
118425
  exports.TestResultSchema = TestResultSchema;
118426
+ exports.ThresholdHelperSpecSchema = require_composition.ThresholdHelperSpecSchema;
117827
118427
  exports.TimelapseRuleInputSchema = TimelapseRuleInputSchema;
117828
118428
  exports.TimelapseRulePatchSchema = TimelapseRulePatchSchema;
117829
118429
  exports.TimelapseRuleSchema = TimelapseRuleSchema;
@@ -117891,6 +118491,7 @@ exports.TransportPlaneSchema = TransportPlaneSchema;
117891
118491
  exports.TurnServerSchema = TurnServerSchema;
117892
118492
  exports.UNATTRIBUTED_BUCKET_KEY = UNATTRIBUTED_BUCKET_KEY;
117893
118493
  exports.UNIT_TABLE = UNIT_TABLE;
118494
+ exports.UNLOCK_CONFIRMATION = UNLOCK_CONFIRMATION;
117894
118495
  exports.USE_ROOT_MODEL_DEFAULTS = USE_ROOT_MODEL_DEFAULTS;
117895
118496
  exports.USE_ROOT_MODEL_SETTING_KEY = USE_ROOT_MODEL_SETTING_KEY;
117896
118497
  exports.UnifiedBrokerInfoSchema = BrokerInfoSchema;
@@ -117989,6 +118590,7 @@ exports.actionsForDeviceType = actionsForDeviceType;
117989
118590
  exports.actionsForDevices = actionsForDevices;
117990
118591
  exports.activateAlarmProfile = activateAlarmProfile;
117991
118592
  exports.activeForReducer = activeForReducer;
118593
+ exports.actuatorMethodRow = actuatorMethodRow;
117992
118594
  exports.addonCatalogHref = addonCatalogHref;
117993
118595
  exports.addonPagesCapability = addonPagesCapability;
117994
118596
  exports.addonPagesSourceCapability = addonPagesSourceCapability;
@@ -118025,6 +118627,7 @@ exports.applyModeDeviceListEdit = applyModeDeviceListEdit;
118025
118627
  exports.applyModelBoundSettings = applyModelBoundSettings;
118026
118628
  exports.applyNonBlockingEdit = applyNonBlockingEdit;
118027
118629
  exports.applySecureOnArmEdit = applySecureOnArmEdit;
118630
+ exports.arithmeticDraft = arithmeticDraft;
118028
118631
  exports.armRemovalReconcile = armRemovalReconcile;
118029
118632
  exports.arrayItemFieldPath = require_sleep.arrayItemFieldPath;
118030
118633
  exports.asBoolean = require_sleep.asBoolean;
@@ -118231,6 +118834,8 @@ exports.defineRecordKinds = require_sleep.defineRecordKinds;
118231
118834
  exports.dehydrateConfigField = require_sleep.dehydrateConfigField;
118232
118835
  exports.dehydrateSchema = require_sleep.dehydrateSchema;
118233
118836
  exports.deleteAlarmProfile = deleteAlarmProfile;
118837
+ exports.derivativeDraft = derivativeDraft;
118838
+ exports.derivativeUnitLabel = derivativeUnitLabel;
118234
118839
  exports.deriveBatteryPresence = deriveBatteryPresence;
118235
118840
  exports.deriveCameraSwitches = deriveCameraSwitches;
118236
118841
  exports.deriveDetailCropRect = deriveDetailCropRect;
@@ -118312,6 +118917,7 @@ exports.evaluateAggregate = evaluateAggregate;
118312
118917
  exports.evaluateAst = require_composition.evaluateAst;
118313
118918
  exports.evaluateCompositionField = evaluateCompositionField;
118314
118919
  exports.evaluateCount = require_composition.evaluateCount;
118920
+ exports.evaluateDerivative = require_composition.evaluateDerivative;
118315
118921
  exports.evaluateExpressionSource = require_composition.evaluateExpressionSource;
118316
118922
  exports.evaluatePoolMemory = evaluatePoolMemory;
118317
118923
  exports.evaluateRose = require_composition.evaluateRose;
@@ -118333,6 +118939,7 @@ exports.expandSystemEventClasses = expandSystemEventClasses;
118333
118939
  exports.exportSubjectOf = exportSubjectOf;
118334
118940
  exports.exposeBlockDevices = exposeBlockDevices;
118335
118941
  exports.expressionContainsBuiltinCall = expressionContainsBuiltinCall;
118942
+ exports.expressionDraft = expressionDraft;
118336
118943
  exports.extensionSlotRef = extensionSlotRef;
118337
118944
  exports.extractNestedAddonId = extractNestedAddonId;
118338
118945
  exports.extractSourceInfoFromMetadata = extractSourceInfoFromMetadata;
@@ -118384,6 +118991,7 @@ exports.gifFilterComplex = gifFilterComplex;
118384
118991
  exports.gifFromMp4Args = gifFromMp4Args;
118385
118992
  exports.globalSecretScope = require_sleep.globalSecretScope;
118386
118993
  exports.grantRowsOf = grantRowsOf;
118994
+ exports.groupActiveCapability = groupActiveCapability;
118387
118995
  exports.groupCommandsCapability = groupCommandsCapability;
118388
118996
  exports.groupStatusCapability = groupStatusCapability;
118389
118997
  exports.groupTemplate = groupTemplate;
@@ -118457,6 +119065,7 @@ exports.isEventKindLabelKey = isEventKindLabelKey;
118457
119065
  exports.isEventOwnerType = isEventOwnerType;
118458
119066
  exports.isFirstLevelMacroClass = isFirstLevelMacroClass;
118459
119067
  exports.isHeldBackTemplateVar = isHeldBackTemplateVar;
119068
+ exports.isHistoryHelper = require_composition.isHistoryHelper;
118460
119069
  exports.isIconKey = isIconKey;
118461
119070
  exports.isIsolatedBuiltin = isIsolatedBuiltin;
118462
119071
  exports.isKeychainStoredField = require_sleep.isKeychainStoredField;
@@ -118562,6 +119171,7 @@ exports.meshNetworkCapability = meshNetworkCapability;
118562
119171
  exports.meterCapability = meterCapability;
118563
119172
  exports.method = require_sleep.method;
118564
119173
  exports.methodAccessForHttpMethod = methodAccessForHttpMethod;
119174
+ exports.methodRefusal = methodRefusal;
118565
119175
  exports.methodRoleOf = methodRoleOf;
118566
119176
  exports.metricsProviderCapability = metricsProviderCapability;
118567
119177
  exports.migrateLegacyDeviceSignalConfig = migrateLegacyDeviceSignalConfig;
@@ -118874,6 +119484,8 @@ exports.settingValuesEqual = settingValuesEqual;
118874
119484
  exports.settingsBelow = settingsBelow;
118875
119485
  exports.settingsStoreCapability = settingsStoreCapability;
118876
119486
  exports.shareViewGrantOf = shareViewGrantOf;
119487
+ exports.sharedActuatorFeatures = sharedActuatorFeatures;
119488
+ exports.sharedActuatorMethods = sharedActuatorMethods;
118877
119489
  exports.sharedColorModes = sharedColorModes;
118878
119490
  exports.sharedLightCaps = sharedLightCaps;
118879
119491
  exports.shortHostname = shortHostname;
@@ -118890,6 +119502,7 @@ exports.smtpProviderCapability = smtpProviderCapability;
118890
119502
  exports.snapshotCapability = snapshotCapability;
118891
119503
  exports.sourcePickForAppliance = require_composition.sourcePickForAppliance;
118892
119504
  exports.sourcePickForHistory = sourcePickForHistory;
119505
+ exports.sourcePickForMath = sourcePickForMath;
118893
119506
  exports.sourcePickForTemplate = sourcePickForTemplate;
118894
119507
  exports.splitTranslationRef = splitTranslationRef;
118895
119508
  exports.squareAround = squareAround;
@@ -118962,6 +119575,7 @@ exports.templateVarSystemEventApplies = templateVarSystemEventApplies;
118962
119575
  exports.templateVarsFor = templateVarsFor;
118963
119576
  exports.terminalSessionCapability = terminalSessionCapability;
118964
119577
  exports.textToHtml = textToHtml;
119578
+ exports.thresholdDraft = thresholdDraft;
118965
119579
  exports.thresholdToBase = thresholdToBase;
118966
119580
  exports.thresholdToDisplay = thresholdToDisplay;
118967
119581
  exports.timeOfDayToMinutes = timeOfDayToMinutes;