@camstack/addon-provider-tuya 0.2.146 → 0.2.148

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.js CHANGED
@@ -819,7 +819,7 @@ async function discoverDevices(options = {}, binder = createDgramBinder()) {
819
819
  return [...byId.values()];
820
820
  }
821
821
  //#endregion
822
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
822
+ //#region ../types/dist/event-category-DmZDPDf_.mjs
823
823
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
824
824
  EventCategory["SystemBoot"] = "system.boot";
825
825
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -1446,7 +1446,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1446
1446
  * (a track was promoted to a stationary-registry entry) or departs (the
1447
1447
  * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
1448
1448
  * the dedicated "Stationary" section — never the live event feed. Payload:
1449
- * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
1449
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
1450
1450
  */
1451
1451
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
1452
1452
  /**
@@ -1584,6 +1584,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1584
1584
  */
1585
1585
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
1586
1586
  /**
1587
+ * An action run started or ended. Emitted by the `actions` surface.
1588
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
1589
+ */
1590
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
1591
+ /**
1587
1592
  * Progress update from a running model conversion job.
1588
1593
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
1589
1594
  * Emitted by `addon-model-studio` on the converting node.
@@ -6262,7 +6267,7 @@ var ZodIssueCode = {
6262
6267
  var ZodFirstPartyTypeKind;
6263
6268
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6264
6269
  //#endregion
6265
- //#region ../types/dist/sleep-bykKrTgu.mjs
6270
+ //#region ../types/dist/sleep-Crlmu_tO.mjs
6266
6271
  /**
6267
6272
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6268
6273
  * window to float samples (D455).
@@ -9382,6 +9387,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
9382
9387
  /** Every capability name, so addon code can check a name without the schema graph. */
9383
9388
  var CAP_NAMES = Object.freeze([
9384
9389
  "accessories",
9390
+ "action-source",
9391
+ "actions",
9385
9392
  "addon-pages",
9386
9393
  "addon-pages-source",
9387
9394
  "addon-routes",
@@ -9455,6 +9462,7 @@ var CAP_NAMES = Object.freeze([
9455
9462
  "filesystem-browse",
9456
9463
  "flood",
9457
9464
  "gas",
9465
+ "group-active",
9458
9466
  "group-commands",
9459
9467
  "group-status",
9460
9468
  "heating-valve",
@@ -9477,6 +9485,7 @@ var CAP_NAMES = Object.freeze([
9477
9485
  "login-method",
9478
9486
  "media-player",
9479
9487
  "mesh-network",
9488
+ "meter",
9480
9489
  "metrics-provider",
9481
9490
  "model-convert",
9482
9491
  "model-distributor",
@@ -9568,11 +9577,11 @@ var CAP_NAMES = Object.freeze([
9568
9577
  Object.freeze({
9569
9578
  "air-quality-sensor": [
9570
9579
  {
9571
- "path": "co2Ppm",
9580
+ "path": "co2",
9572
9581
  "valueType": "number"
9573
9582
  },
9574
9583
  {
9575
- "path": "vocPpb",
9584
+ "path": "voc",
9576
9585
  "valueType": "number"
9577
9586
  },
9578
9587
  {
@@ -9622,7 +9631,7 @@ Object.freeze({
9622
9631
  }
9623
9632
  ],
9624
9633
  "ambient-light-sensor": [{
9625
- "path": "lux",
9634
+ "path": "illuminance",
9626
9635
  "valueType": "number"
9627
9636
  }],
9628
9637
  "audio-metrics": [
@@ -9683,7 +9692,7 @@ Object.freeze({
9683
9692
  ],
9684
9693
  "battery": [
9685
9694
  {
9686
- "path": "percentage",
9695
+ "path": "level",
9687
9696
  "valueType": "number"
9688
9697
  },
9689
9698
  {
@@ -9705,7 +9714,7 @@ Object.freeze({
9705
9714
  "valueType": "number"
9706
9715
  },
9707
9716
  {
9708
- "path": "volts",
9717
+ "path": "voltage",
9709
9718
  "valueType": "number"
9710
9719
  },
9711
9720
  {
@@ -9726,7 +9735,7 @@ Object.freeze({
9726
9735
  "valueType": "boolean"
9727
9736
  }],
9728
9737
  "brightness": [{
9729
- "path": "percentage",
9738
+ "path": "level",
9730
9739
  "valueType": "number"
9731
9740
  }],
9732
9741
  "broker": [{
@@ -9927,7 +9936,7 @@ Object.freeze({
9927
9936
  "valueType": "number"
9928
9937
  },
9929
9938
  {
9930
- "path": "mireds",
9939
+ "path": "colorTemperature",
9931
9940
  "valueType": "number"
9932
9941
  }
9933
9942
  ],
@@ -10106,15 +10115,15 @@ Object.freeze({
10106
10115
  "valueType": "string"
10107
10116
  },
10108
10117
  {
10109
- "path": "remainingSeconds",
10118
+ "path": "remaining",
10110
10119
  "valueType": "number"
10111
10120
  },
10112
10121
  {
10113
- "path": "delayedStartSeconds",
10122
+ "path": "delayedStart",
10114
10123
  "valueType": "number"
10115
10124
  },
10116
10125
  {
10117
- "path": "progressPercent",
10126
+ "path": "progress",
10118
10127
  "valueType": "number"
10119
10128
  },
10120
10129
  {
@@ -10228,7 +10237,7 @@ Object.freeze({
10228
10237
  ],
10229
10238
  "fan-control": [
10230
10239
  {
10231
- "path": "percentage",
10240
+ "path": "speed",
10232
10241
  "valueType": "number"
10233
10242
  },
10234
10243
  {
@@ -10275,6 +10284,10 @@ Object.freeze({
10275
10284
  "path": "detected",
10276
10285
  "valueType": "boolean"
10277
10286
  }],
10287
+ "group-active": [{
10288
+ "path": "active",
10289
+ "valueType": "boolean"
10290
+ }],
10278
10291
  "group-status": [
10279
10292
  {
10280
10293
  "path": "included",
@@ -10324,7 +10337,7 @@ Object.freeze({
10324
10337
  }
10325
10338
  ],
10326
10339
  "humidity-sensor": [{
10327
- "path": "percent",
10340
+ "path": "humidity",
10328
10341
  "valueType": "number"
10329
10342
  }],
10330
10343
  "image": [{
@@ -10434,7 +10447,7 @@ Object.freeze({
10434
10447
  "valueType": "number"
10435
10448
  },
10436
10449
  {
10437
- "path": "progressPercent",
10450
+ "path": "progress",
10438
10451
  "valueType": "number"
10439
10452
  },
10440
10453
  {
@@ -10513,11 +10526,11 @@ Object.freeze({
10513
10526
  "valueType": "string"
10514
10527
  },
10515
10528
  {
10516
- "path": "positionMs",
10529
+ "path": "position",
10517
10530
  "valueType": "number"
10518
10531
  },
10519
10532
  {
10520
- "path": "durationMs",
10533
+ "path": "duration",
10521
10534
  "valueType": "number"
10522
10535
  },
10523
10536
  {
@@ -10534,6 +10547,10 @@ Object.freeze({
10534
10547
  ]
10535
10548
  }
10536
10549
  ],
10550
+ "meter": [{
10551
+ "path": "value",
10552
+ "valueType": "number"
10553
+ }],
10537
10554
  "motion": [
10538
10555
  {
10539
10556
  "path": "detected",
@@ -10615,11 +10632,11 @@ Object.freeze({
10615
10632
  ]
10616
10633
  },
10617
10634
  {
10618
- "path": "signalPercent",
10635
+ "path": "signal",
10619
10636
  "valueType": "number"
10620
10637
  },
10621
10638
  {
10622
- "path": "rssiDbm",
10639
+ "path": "rssi",
10623
10640
  "valueType": "number"
10624
10641
  },
10625
10642
  {
@@ -10674,6 +10691,18 @@ Object.freeze({
10674
10691
  "path": "foodLevel",
10675
10692
  "valueType": "number"
10676
10693
  },
10694
+ {
10695
+ "path": "bowlFood",
10696
+ "valueType": "number"
10697
+ },
10698
+ {
10699
+ "path": "eating",
10700
+ "valueType": "boolean"
10701
+ },
10702
+ {
10703
+ "path": "supportsMarkRefilled",
10704
+ "valueType": "boolean"
10705
+ },
10677
10706
  {
10678
10707
  "path": "food1",
10679
10708
  "valueType": "number"
@@ -10738,23 +10767,23 @@ Object.freeze({
10738
10767
  ],
10739
10768
  "power-meter": [
10740
10769
  {
10741
- "path": "watts",
10770
+ "path": "power",
10742
10771
  "valueType": "number"
10743
10772
  },
10744
10773
  {
10745
- "path": "kwhTotal",
10774
+ "path": "energy",
10746
10775
  "valueType": "number"
10747
10776
  },
10748
10777
  {
10749
- "path": "volts",
10778
+ "path": "voltage",
10750
10779
  "valueType": "number"
10751
10780
  },
10752
10781
  {
10753
- "path": "amps",
10782
+ "path": "current",
10754
10783
  "valueType": "number"
10755
10784
  },
10756
10785
  {
10757
- "path": "va",
10786
+ "path": "apparentPower",
10758
10787
  "valueType": "number"
10759
10788
  },
10760
10789
  {
@@ -10782,14 +10811,10 @@ Object.freeze({
10782
10811
  {
10783
10812
  "path": "gps.accuracyMeters",
10784
10813
  "valueType": "number"
10785
- },
10786
- {
10787
- "path": "batteryPercent",
10788
- "valueType": "number"
10789
10814
  }
10790
10815
  ],
10791
10816
  "pressure-sensor": [{
10792
- "path": "hpa",
10817
+ "path": "pressure",
10793
10818
  "valueType": "number"
10794
10819
  }],
10795
10820
  "privacy-mask": [{
@@ -11103,7 +11128,7 @@ Object.freeze({
11103
11128
  "valueType": "boolean"
11104
11129
  }],
11105
11130
  "temperature-sensor": [{
11106
- "path": "celsius",
11131
+ "path": "temperature",
11107
11132
  "valueType": "number"
11108
11133
  }],
11109
11134
  "update": [
@@ -11151,7 +11176,7 @@ Object.freeze({
11151
11176
  "valueType": "string"
11152
11177
  },
11153
11178
  {
11154
- "path": "progressPercent",
11179
+ "path": "progress",
11155
11180
  "valueType": "number"
11156
11181
  },
11157
11182
  {
@@ -11277,7 +11302,7 @@ object({
11277
11302
  params: record(string(), union([string(), number()])).optional()
11278
11303
  });
11279
11304
  //#endregion
11280
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
11305
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
11281
11306
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
11282
11307
  DeviceType["Camera"] = "camera";
11283
11308
  DeviceType["Hub"] = "hub";
@@ -11371,8 +11396,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
11371
11396
  DeviceType["PetFeeder"] = "pet-feeder";
11372
11397
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
11373
11398
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
11374
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
11399
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
11400
+ * A plug-built dishwasher carries running-state and power instead. */
11375
11401
  DeviceType["Dishwasher"] = "dishwasher";
11402
+ /** Washing machine. A plug-built one is running while its power stays
11403
+ * above a threshold. No program cap — that surface is the dishwasher's. */
11404
+ DeviceType["Washer"] = "washer";
11405
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
11406
+ DeviceType["Dryer"] = "dryer";
11376
11407
  return DeviceType;
11377
11408
  }({});
11378
11409
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11630,7 +11661,7 @@ function errMsg(err) {
11630
11661
  return String(err);
11631
11662
  }
11632
11663
  //#endregion
11633
- //#region ../types/dist/composition-OhpuespA.mjs
11664
+ //#region ../types/dist/composition-CZImLpOI.mjs
11634
11665
  /**
11635
11666
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11636
11667
  *
@@ -11945,6 +11976,13 @@ var table = {
11945
11976
  apply: () => {
11946
11977
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11947
11978
  }
11979
+ },
11980
+ schmitt: {
11981
+ minArgs: 3,
11982
+ maxArgs: 5,
11983
+ apply: () => {
11984
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11985
+ }
11948
11986
  }
11949
11987
  };
11950
11988
  Object.freeze(Object.assign(Object.create(null), table));
@@ -12386,6 +12424,99 @@ function validateExpressionSource(src) {
12386
12424
  return null;
12387
12425
  }
12388
12426
  /**
12427
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
12428
+ *
12429
+ * Standby of the control board is under 5 W; a motor or a heater is far
12430
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
12431
+ * a cycle, so running starts only after 30 s above the line.
12432
+ *
12433
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
12434
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
12435
+ * report "finished" in the middle of the program, so the stop dwell is
12436
+ * 10 minutes on every type. The number is stored per type so one machine
12437
+ * can move later without moving the others.
12438
+ */
12439
+ var APPLIANCE_TEMPLATE_ID = "appliance";
12440
+ var APPLIANCE_KINDS = [
12441
+ "washer",
12442
+ "dryer",
12443
+ "dishwasher"
12444
+ ];
12445
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
12446
+ /**
12447
+ * Statistics and integrals over a history read (D944).
12448
+ *
12449
+ * Pure: the same points always give the same answer, so a restart recomputes
12450
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
12451
+ * never the mean of the samples. A hole with no held value is not integrated
12452
+ * as zero. A truncated read is not published.
12453
+ */
12454
+ var STATISTIC_STATS = [
12455
+ "min",
12456
+ "max",
12457
+ "mean",
12458
+ "sum",
12459
+ "count",
12460
+ "change"
12461
+ ];
12462
+ /**
12463
+ * The range a statistics or integral helper asks history for (D944).
12464
+ *
12465
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
12466
+ * or 25 hours across a DST change. A tier is eligible only when its retention
12467
+ * is strictly longer than the longest that range can be. Forever qualifies.
12468
+ * Equal to a tier's retention is a refusal, not a short answer.
12469
+ */
12470
+ var MINUTE_MS = 6e4;
12471
+ var HOUR_MS = 60 * MINUTE_MS;
12472
+ var DAY_MS = 24 * HOUR_MS;
12473
+ var ROLLING_SPANS = [
12474
+ "1h",
12475
+ "24h",
12476
+ "2d",
12477
+ "7d",
12478
+ "30d",
12479
+ "custom"
12480
+ ];
12481
+ var CALENDAR_PERIODS = [
12482
+ "today",
12483
+ "yesterday",
12484
+ "this-week",
12485
+ "this-month"
12486
+ ];
12487
+ var RESET_PERIODS = [
12488
+ "never",
12489
+ "daily",
12490
+ "weekly",
12491
+ "monthly"
12492
+ ];
12493
+ 5 * MINUTE_MS;
12494
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
12495
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
12496
+ var rollingWindowObject = object({
12497
+ kind: literal("rolling"),
12498
+ span: _enum(ROLLING_SPANS),
12499
+ customMinutes: number().int().min(5).optional()
12500
+ }).check((payload) => {
12501
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
12502
+ code: "custom",
12503
+ message: "a custom range needs a duration in minutes",
12504
+ path: ["customMinutes"],
12505
+ input: payload.value
12506
+ });
12507
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
12508
+ code: "custom",
12509
+ message: "only a custom range carries a duration",
12510
+ path: ["customMinutes"],
12511
+ input: payload.value
12512
+ });
12513
+ });
12514
+ var RollingWindowSchema = rollingWindowObject;
12515
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
12516
+ kind: literal("calendar"),
12517
+ period: _enum(CALENDAR_PERIODS)
12518
+ })]);
12519
+ /**
12389
12520
  * A composition source identity: `(addonId, stableId)`, never a numeric device
12390
12521
  * id. Split out so helper schemas can name it without a cycle through
12391
12522
  * `composition.ts` (D659).
@@ -12411,7 +12542,13 @@ var GROUP_TEMPLATE_IDS = [
12411
12542
  "occupancy",
12412
12543
  "binary",
12413
12544
  "switch",
12414
- "light"
12545
+ "light",
12546
+ "fan",
12547
+ "cover",
12548
+ "valve",
12549
+ "lock",
12550
+ "media-player",
12551
+ "climate"
12415
12552
  ];
12416
12553
  var NUMERIC_AGGREGATES = [
12417
12554
  "mean",
@@ -12441,14 +12578,73 @@ var HelperMemberSchema = object({
12441
12578
  cap: string().min(1),
12442
12579
  fieldPath: string().min(1)
12443
12580
  });
12444
- var HelperSpecSchema = discriminatedUnion("kind", [object({
12445
- kind: literal("group"),
12446
- templateId: GroupTemplateIdSchema,
12447
- aggregate: AggregateReducerSchema,
12448
- members: array(HelperMemberSchema).min(1).max(64),
12449
- /** Actuator groups only. Readings leave this unset. */
12450
- active: ActiveCriterionSchema.optional()
12451
- })]);
12581
+ var HelperSpecSchema = discriminatedUnion("kind", [
12582
+ object({
12583
+ kind: literal("group"),
12584
+ templateId: GroupTemplateIdSchema,
12585
+ aggregate: AggregateReducerSchema,
12586
+ members: array(HelperMemberSchema).min(1).max(64),
12587
+ /** Actuator groups only. Readings leave this unset. */
12588
+ active: ActiveCriterionSchema.optional()
12589
+ }),
12590
+ object({
12591
+ kind: literal("appliance"),
12592
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
12593
+ appliance: _enum(APPLIANCE_KINDS),
12594
+ thresholdWatts: number().positive(),
12595
+ startDwellMs: number().int().nonnegative(),
12596
+ stopDwellMs: number().int().nonnegative(),
12597
+ member: HelperMemberSchema
12598
+ }),
12599
+ object({
12600
+ kind: literal("statistic"),
12601
+ stat: _enum(STATISTIC_STATS),
12602
+ window: StatisticWindowSchema,
12603
+ source: HelperMemberSchema
12604
+ }),
12605
+ object({
12606
+ kind: literal("integral"),
12607
+ reset: _enum(RESET_PERIODS),
12608
+ output: _enum(["energy", "meter"]),
12609
+ source: HelperMemberSchema
12610
+ }),
12611
+ object({
12612
+ kind: literal("threshold"),
12613
+ source: HelperMemberSchema,
12614
+ high: number(),
12615
+ low: number(),
12616
+ onForMs: number().int().nonnegative(),
12617
+ offForMs: number().int().nonnegative()
12618
+ }),
12619
+ object({
12620
+ kind: literal("arithmetic"),
12621
+ op: _enum([
12622
+ "sum",
12623
+ "difference",
12624
+ "ratio"
12625
+ ]),
12626
+ inputs: array(HelperMemberSchema).min(2).max(64)
12627
+ }),
12628
+ object({
12629
+ kind: literal("expression"),
12630
+ expr: string().min(1),
12631
+ inputs: array(object({
12632
+ name: string().min(1),
12633
+ member: HelperMemberSchema
12634
+ })).min(1).max(64),
12635
+ output: _enum(["number", "boolean"])
12636
+ }),
12637
+ object({
12638
+ kind: literal("derivative"),
12639
+ window: RollingWindowSchema,
12640
+ per: _enum([
12641
+ "second",
12642
+ "minute",
12643
+ "hour"
12644
+ ]),
12645
+ source: HelperMemberSchema
12646
+ })
12647
+ ]);
12452
12648
  /** A composed device's stableId is this prefix plus the block id. */
12453
12649
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
12454
12650
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -12509,6 +12705,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12509
12705
  "all"
12510
12706
  ]),
12511
12707
  reads: array(CompositionFieldReadSchema).min(1).max(64),
12708
+ /**
12709
+ * Set when the reads are an enum state, not a yes/no: the listed states
12710
+ * count as active (a cover is `open`). A state outside the list is inactive;
12711
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
12712
+ */
12713
+ activeWhen: array(string().min(1)).min(1).optional(),
12512
12714
  /** `value` is the reading; the others are the member counts. */
12513
12715
  publish: _enum([
12514
12716
  "value",
@@ -12522,6 +12724,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12522
12724
  "omit",
12523
12725
  "offline"
12524
12726
  ])
12727
+ }),
12728
+ object({
12729
+ kind: literal("series"),
12730
+ read: CompositionFieldReadSchema,
12731
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12732
+ publish: _enum([
12733
+ "value",
12734
+ "included",
12735
+ "excluded",
12736
+ "total"
12737
+ ]),
12738
+ /**
12739
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12740
+ * device unseeded until history answers, so a restart cannot publish 0.
12741
+ */
12742
+ empty: _enum([
12743
+ "null",
12744
+ "omit",
12745
+ "offline"
12746
+ ])
12525
12747
  })
12526
12748
  ]);
12527
12749
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -15959,6 +16181,14 @@ var AutomationConditionLeafSchema = object({
15959
16181
  array(union([string(), number()]))
15960
16182
  ])
15961
16183
  });
16184
+ var AutomationConditionStationaryPresentSchema = object({
16185
+ kind: literal("stationary-present"),
16186
+ deviceId: number().int().nonnegative(),
16187
+ /** Class names; a macro class matches every raw class that maps to it. */
16188
+ classes: array(string().min(1)).min(1).optional(),
16189
+ /** The parked object must be in at least one of these zones. */
16190
+ zones: array(string().min(1)).min(1).optional()
16191
+ });
15962
16192
  /**
15963
16193
  * The expression leaf, declared as a plain object rather than an intersection
15964
16194
  * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
@@ -15992,7 +16222,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
15992
16222
  child: AutomationConditionSchema
15993
16223
  }),
15994
16224
  AutomationConditionLeafSchema,
15995
- AutomationConditionExpressionSchema
16225
+ AutomationConditionExpressionSchema,
16226
+ AutomationConditionStationaryPresentSchema
15996
16227
  ]));
15997
16228
  /**
15998
16229
  * What starts a run.
@@ -16033,7 +16264,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16033
16264
  kind: literal("schedule"),
16034
16265
  cron: string().min(1).max(120)
16035
16266
  }),
16036
- object({ kind: literal("manual") })
16267
+ object({ kind: literal("manual") }),
16268
+ object({
16269
+ kind: literal("detection"),
16270
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
16271
+ * matches every raw class that maps to it; any other name matches
16272
+ * exactly. */
16273
+ classes: array(string().min(1)).min(1),
16274
+ deviceIds: array(number().int().nonnegative()).min(1),
16275
+ /** The track must have visited at least one of these zones. */
16276
+ zones: array(string().min(1)).min(1).optional(),
16277
+ /** Best-so-far confidence the track must reach. */
16278
+ minScore: number().min(0).max(1).optional()
16279
+ }),
16280
+ object({
16281
+ kind: literal("stationary"),
16282
+ deviceIds: array(number().int().nonnegative()).min(1),
16283
+ /** Class names; a macro class matches like the detection trigger. */
16284
+ classes: array(string().min(1)).min(1).optional(),
16285
+ /** The parked object must be in at least one of these zones. */
16286
+ zones: array(string().min(1)).min(1).optional(),
16287
+ /** `appeared`: it became parked. `departed`: the registry retired it.
16288
+ * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
16289
+ phase: _enum(["appeared", "departed"])
16290
+ }),
16291
+ object({
16292
+ kind: literal("interval"),
16293
+ minutes: number().int().min(1).max(1440)
16294
+ })
16037
16295
  ]);
16038
16296
  /**
16039
16297
  * One action step.
@@ -16053,28 +16311,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16053
16311
  * is called. The runner still receives a number and is untouched; the
16054
16312
  * resolution is the recipe's job, not the runner's.
16055
16313
  */
16314
+ var AutomationWaitStepSchema = object({
16315
+ kind: literal("wait"),
16316
+ seconds: number().min(0).max(300)
16317
+ });
16318
+ var AutomationCapStepSchema = object({
16319
+ kind: literal("cap"),
16320
+ /**
16321
+ * One device, several same-family devices (locks / climate family / …),
16322
+ * or `{ $var }` for the device that triggered the run. An array is
16323
+ * fan-out at codegen — each id gets the same cap.method call.
16324
+ */
16325
+ deviceId: union([
16326
+ number().int(),
16327
+ array(number().int()).min(1),
16328
+ object({ $var: string().min(1) })
16329
+ ]),
16330
+ cap: string().min(1),
16331
+ method: string().min(1),
16332
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
16333
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
16334
+ * the expression leaf. */
16335
+ args: record(string(), unknown()).optional()
16336
+ });
16337
+ /**
16338
+ * The steps an internal Action may contain: cap and wait, and nothing that can
16339
+ * call back into an Action — so an Action can never reach itself (no cycles).
16340
+ */
16341
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
16056
16342
  var AutomationActionSchema = discriminatedUnion("kind", [
16343
+ AutomationWaitStepSchema,
16344
+ AutomationCapStepSchema,
16057
16345
  object({
16058
- kind: literal("wait"),
16059
- seconds: number().min(0).max(300)
16060
- }),
16061
- object({
16062
- kind: literal("cap"),
16063
- /**
16064
- * One device, several same-family devices (locks / climate family / …),
16065
- * or `{ $var }` for the device that triggered the run. An array is
16066
- * fan-out at codegen — each id gets the same cap.method call.
16067
- */
16068
- deviceId: union([
16069
- number().int(),
16070
- array(number().int()).min(1),
16071
- object({ $var: string().min(1) })
16072
- ]),
16073
- cap: string().min(1),
16074
- method: string().min(1),
16075
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
16076
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
16077
- args: record(string(), unknown()).optional()
16346
+ kind: literal("action"),
16347
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
16348
+ actionId: string().min(1),
16349
+ /** Values for the action's declared variables; the others take their default. */
16350
+ variables: record(string(), union([
16351
+ string(),
16352
+ number(),
16353
+ boolean()
16354
+ ])).optional()
16078
16355
  }),
16079
16356
  object({
16080
16357
  kind: literal("code"),
@@ -16083,10 +16360,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
16083
16360
  code: string().min(1).max(2e4)
16084
16361
  })
16085
16362
  ]);
16363
+ /**
16364
+ * How a trigger that arrives while the automation is busy (or cooling down) is
16365
+ * handled. One of the automation's execution modes, never a property of its
16366
+ * actions. Absent means `single`.
16367
+ *
16368
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
16369
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
16370
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
16371
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
16372
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
16373
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
16374
+ * wait, never the single/queued limits.
16375
+ */
16376
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
16377
+ object({ mode: literal("single") }),
16378
+ object({ mode: literal("restart") }),
16379
+ object({
16380
+ mode: literal("queued"),
16381
+ max: number().int().min(1).max(10)
16382
+ }),
16383
+ object({
16384
+ mode: literal("parallel"),
16385
+ max: number().int().min(1).max(10)
16386
+ }),
16387
+ object({
16388
+ mode: literal("cooldown"),
16389
+ minutes: number().int().min(1).max(1440)
16390
+ })
16391
+ ]);
16086
16392
  var AutomationRecipeSchema = object({
16087
16393
  triggers: array(AutomationTriggerSchema),
16088
16394
  conditions: AutomationConditionSchema.optional(),
16089
- actions: array(AutomationActionSchema)
16395
+ actions: array(AutomationActionSchema),
16396
+ runMode: AutomationRunModeSchema.optional(),
16397
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
16398
+ keepTraces: number().int().min(1).max(100).optional()
16399
+ });
16400
+ new Set([
16401
+ {
16402
+ id: "person",
16403
+ name: "Person"
16404
+ },
16405
+ {
16406
+ id: "vehicle",
16407
+ name: "Vehicle"
16408
+ },
16409
+ {
16410
+ id: "animal",
16411
+ name: "Animal"
16412
+ },
16413
+ {
16414
+ id: "package",
16415
+ name: "Package"
16416
+ }
16417
+ ].map((l) => l.id));
16418
+ var COCO_TO_MACRO = {
16419
+ mapping: {
16420
+ person: "person",
16421
+ bicycle: "vehicle",
16422
+ car: "vehicle",
16423
+ motorcycle: "vehicle",
16424
+ airplane: "vehicle",
16425
+ bus: "vehicle",
16426
+ train: "vehicle",
16427
+ truck: "vehicle",
16428
+ boat: "vehicle",
16429
+ bird: "animal",
16430
+ cat: "animal",
16431
+ dog: "animal",
16432
+ horse: "animal",
16433
+ sheep: "animal",
16434
+ cow: "animal",
16435
+ elephant: "animal",
16436
+ bear: "animal",
16437
+ zebra: "animal",
16438
+ giraffe: "animal",
16439
+ suitcase: "package",
16440
+ backpack: "package",
16441
+ handbag: "package"
16442
+ },
16443
+ preserveOriginal: false
16444
+ };
16445
+ new Set(Object.values(COCO_TO_MACRO.mapping));
16446
+ /**
16447
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
16448
+ * so the editor can paint every block green or red with its reason.
16449
+ *
16450
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
16451
+ * every condition node (containers and leaves, including the ones a
16452
+ * short-circuit never reached), the run-mode verdict and every action step —
16453
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
16454
+ * for an Action run). Skipped
16455
+ * evaluations are traced too, and identical consecutive skips collapse into a
16456
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
16457
+ *
16458
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
16459
+ * vocabulary so an Action run records the same step shape.
16460
+ */
16461
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
16462
+ var TraceValueSchema = union([
16463
+ string(),
16464
+ number(),
16465
+ boolean(),
16466
+ _null(),
16467
+ array(union([string(), number()]))
16468
+ ]);
16469
+ var AutomationRunOutcomeSchema = _enum([
16470
+ "ran",
16471
+ "skipped-trigger",
16472
+ "skipped-conditions",
16473
+ "skipped-cooldown",
16474
+ "skipped-overlap",
16475
+ "failed",
16476
+ "aborted"
16477
+ ]);
16478
+ /** `not-evaluated` = a short-circuit never reached the node. */
16479
+ var TraceNodeOutcomeSchema = _enum([
16480
+ "pass",
16481
+ "fail",
16482
+ "not-evaluated"
16483
+ ]);
16484
+ var AutomationTriggerTraceSchema = object({
16485
+ kind: string().min(1),
16486
+ outcome: _enum(["pass", "fail"]),
16487
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
16488
+ reason: string(),
16489
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
16490
+ vars: record(string(), TraceValueSchema)
16491
+ });
16492
+ var AutomationConditionTraceNodeSchema = object({
16493
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
16494
+ path: string().min(1),
16495
+ kind: _enum([
16496
+ "all",
16497
+ "any",
16498
+ "not",
16499
+ "condition",
16500
+ "expression",
16501
+ "stationary-present"
16502
+ ]),
16503
+ outcome: TraceNodeOutcomeSchema,
16504
+ reason: string(),
16505
+ deviceId: number().int().optional(),
16506
+ cap: string().optional(),
16507
+ fieldPath: string().optional(),
16508
+ operator: string().optional(),
16509
+ expected: TraceValueSchema.optional(),
16510
+ /** What the mirror held; `null` when it held nothing (unknown). */
16511
+ observed: TraceValueSchema.optional()
16512
+ });
16513
+ var AutomationRunVerdictSchema = object({
16514
+ mode: AutomationRunModeSchema,
16515
+ decision: _enum([
16516
+ "run",
16517
+ "queued",
16518
+ "restart",
16519
+ "drop",
16520
+ "cooldown",
16521
+ "bypass"
16522
+ ]),
16523
+ reason: string()
16524
+ });
16525
+ /** One executed (or not-run) step — shared by automations and Action runs. */
16526
+ var AutomationStepTraceSchema = object({
16527
+ /** `actions.<i>` */
16528
+ path: string().min(1),
16529
+ kind: string().min(1),
16530
+ cap: string().optional(),
16531
+ method: string().optional(),
16532
+ deviceId: number().int().optional(),
16533
+ startedAt: number().optional(),
16534
+ endedAt: number().optional(),
16535
+ outcome: _enum([
16536
+ "ok",
16537
+ "error",
16538
+ "not-run"
16539
+ ]),
16540
+ message: string().optional()
16541
+ });
16542
+ var AutomationRunTraceSchema = object({
16543
+ runId: string().min(1),
16544
+ startedAt: number(),
16545
+ endedAt: number(),
16546
+ trigger: AutomationTriggerTraceSchema,
16547
+ conditions: array(AutomationConditionTraceNodeSchema),
16548
+ verdict: AutomationRunVerdictSchema.optional(),
16549
+ steps: array(AutomationStepTraceSchema),
16550
+ outcome: AutomationRunOutcomeSchema,
16551
+ /** How many identical consecutive skips this trace stands for (>= 1). */
16552
+ repeat: number().int().min(1),
16553
+ /** When the latest of those repeats happened. */
16554
+ lastAt: number()
16555
+ });
16556
+ /**
16557
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
16558
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
16559
+ */
16560
+ var AutomationStateDocSchema = object({
16561
+ version: literal(1),
16562
+ lastRunStartedAt: number().nullable(),
16563
+ traces: array(AutomationRunTraceSchema)
16564
+ });
16565
+ /**
16566
+ * Actions — named, runnable things an operator (or an automation) can start,
16567
+ * declared as LIST ENTRIES, never as devices.
16568
+ *
16569
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
16570
+ * hub's `actions` record kind) or declared by an addon through the
16571
+ * `action-source` collection (a Home Assistant script, a scene …). The one
16572
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
16573
+ * catalog of its own (D3) — every `list` asks the providers live.
16574
+ *
16575
+ * This file is the data half: the entry schema, the variables an entry may
16576
+ * declare, and the pure resolution of the values a run was given. It holds no
16577
+ * runtime.
16578
+ */
16579
+ /** What a variable's value is. `select` takes one of `options`. */
16580
+ var ActionVariableTypeSchema = _enum([
16581
+ "string",
16582
+ "number",
16583
+ "boolean",
16584
+ "select"
16585
+ ]);
16586
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
16587
+ var ActionVariableValueSchema = union([
16588
+ string(),
16589
+ number(),
16590
+ boolean()
16591
+ ]);
16592
+ /**
16593
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
16594
+ * value) or `{{vars.name}}` (inside a string) in their args.
16595
+ */
16596
+ var ActionVariableSchema = object({
16597
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
16598
+ label: string().min(1).max(120).optional(),
16599
+ type: ActionVariableTypeSchema,
16600
+ required: boolean().optional(),
16601
+ default: ActionVariableValueSchema.optional(),
16602
+ /** The allowed values of a `select`. */
16603
+ options: array(string().min(1)).min(1).optional()
16604
+ }).superRefine((v, ctx) => {
16605
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
16606
+ code: "custom",
16607
+ message: "a select variable needs options",
16608
+ path: ["options"]
16609
+ });
16610
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
16611
+ code: "custom",
16612
+ message: variableValueError(v, v.default) ?? "invalid default",
16613
+ path: ["default"]
16614
+ });
16615
+ });
16616
+ /** Why `value` is not a legal value for the variable, or null when it is. */
16617
+ function variableValueError(variable, value) {
16618
+ switch (variable.type) {
16619
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
16620
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
16621
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
16622
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
16623
+ }
16624
+ }
16625
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
16626
+ function isInternalActionId(actionId) {
16627
+ return actionId.startsWith("internal:") && actionId.length > 9;
16628
+ }
16629
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
16630
+ function addonIdOfActionId(actionId) {
16631
+ if (isInternalActionId(actionId)) return null;
16632
+ const at = actionId.indexOf(":");
16633
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
16634
+ }
16635
+ var ActionStatusSchema = object({
16636
+ isRunning: boolean(),
16637
+ /** Ms epoch the latest run STARTED; null when it never ran. */
16638
+ lastRunAt: number().nullable(),
16639
+ /** The latest run's failure; null on success or when it never ran. */
16640
+ lastError: string().nullable()
16641
+ });
16642
+ var ActionEntrySchema = object({
16643
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
16644
+ id: string().min(3),
16645
+ name: string().min(1).max(120),
16646
+ /** The addon that declared it; `core-blocks` for an internal one. */
16647
+ addonId: string().min(1),
16648
+ source: ActionSourceKindSchema,
16649
+ /** An addon-declared entry is edited where it was declared. */
16650
+ readOnly: boolean(),
16651
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
16652
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
16653
+ variables: array(ActionVariableSchema).max(20).optional(),
16654
+ status: ActionStatusSchema
16655
+ }).superRefine((entry, ctx) => {
16656
+ if (entry.source === "internal") {
16657
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
16658
+ code: "custom",
16659
+ message: "an internal action id is internal:<uuid>",
16660
+ path: ["id"]
16661
+ });
16662
+ return;
16663
+ }
16664
+ if (entry.steps !== void 0) ctx.addIssue({
16665
+ code: "custom",
16666
+ message: "only an internal action carries steps",
16667
+ path: ["steps"]
16668
+ });
16669
+ if (!entry.readOnly) ctx.addIssue({
16670
+ code: "custom",
16671
+ message: "an addon action is read-only here",
16672
+ path: ["readOnly"]
16673
+ });
16674
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
16675
+ code: "custom",
16676
+ message: "an addon action id is <addonId>:<localId>",
16677
+ path: ["id"]
16678
+ });
16679
+ });
16680
+ /** What an operator writes for an internal action. */
16681
+ var ActionInputSchema = object({
16682
+ name: string().min(1).max(120),
16683
+ steps: array(ActionStepSchema).min(1).max(50),
16684
+ variables: array(ActionVariableSchema).max(20).optional()
16685
+ });
16686
+ /** Who started a run. Absent = the operator pressed it. */
16687
+ var ActionRunOriginSchema = object({
16688
+ /** The automation block whose step started it. */
16689
+ blockId: string().min(1).optional() });
16690
+ /** `started` = the caller did not ask to wait. */
16691
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
16692
+ var ActionRunResultSchema = object({
16693
+ runId: string().min(1),
16694
+ outcome: ActionRunOutcomeSchema,
16695
+ /** Why it did not run, or what failed. */
16696
+ message: string().optional()
16697
+ });
16698
+ _enum([
16699
+ "not-found",
16700
+ "read-only",
16701
+ "invalid-variables",
16702
+ "invalid-steps",
16703
+ "not-linked",
16704
+ "busy"
16705
+ ]);
16706
+ object({
16707
+ actionId: string(),
16708
+ runId: string(),
16709
+ isRunning: boolean(),
16710
+ /** Set when the run ended. */
16711
+ outcome: AutomationRunOutcomeSchema.optional(),
16712
+ lastRunAt: number().nullable(),
16713
+ lastError: string().nullable()
16090
16714
  });
16091
16715
  /**
16092
16716
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -17681,7 +18305,22 @@ var BrokerProviderInfoSchema = object({
17681
18305
  */
17682
18306
  interactiveConnectPath: string().min(1).optional(),
17683
18307
  /** Relative hub path to disconnect the linked account (same popup pattern). */
17684
- interactiveDisconnectPath: string().min(1).optional()
18308
+ interactiveDisconnectPath: string().min(1).optional(),
18309
+ /**
18310
+ * The kind signs in through a server-held interactive session instead of
18311
+ * (or before) typed credentials. `basePath` is a hub-relative route set
18312
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
18313
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
18314
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
18315
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
18316
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
18317
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
18318
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
18319
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
18320
+ * offers it as the default path; the fields the session fills are
18321
+ * marked `filledBySignIn` in the kind's settings schema.
18322
+ */
18323
+ signIn: object({ basePath: string().min(1) }).optional()
17685
18324
  }))
17686
18325
  });
17687
18326
  var ListInputSchema = object({
@@ -18758,7 +19397,7 @@ var CompositionEventPlanSchema = object({
18758
19397
  stateField: string(),
18759
19398
  fieldKey: string()
18760
19399
  });
18761
- /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
19400
+ /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
18762
19401
  var CompositionProblemSchema = object({
18763
19402
  code: _enum([
18764
19403
  "reserved-variant",
@@ -18786,7 +19425,8 @@ var CompositionProblemSchema = object({
18786
19425
  "native-capability-replace",
18787
19426
  "mixed-quantity",
18788
19427
  "helper-mismatch",
18789
- "member-constraint"
19428
+ "member-constraint",
19429
+ "retention"
18790
19430
  ]),
18791
19431
  path: string(),
18792
19432
  message: string()
@@ -19020,13 +19660,17 @@ Object.freeze({
19020
19660
  latchedAt: {
19021
19661
  kind: "fixed",
19022
19662
  result: ["number", "null"]
19663
+ },
19664
+ schmitt: {
19665
+ kind: "fixed",
19666
+ result: ["boolean", "null"]
19023
19667
  }
19024
19668
  });
19025
19669
  /**
19026
19670
  * One catalog row per base reading a group helper can build (D944 §3).
19027
19671
  * Data, not a form default written into the block.
19028
19672
  */
19029
- var NOW = {
19673
+ var NOW$2 = {
19030
19674
  fieldPath: "lastFetchedAt",
19031
19675
  expr: "now"
19032
19676
  };
@@ -19040,6 +19684,8 @@ function numeric(input) {
19040
19684
  family: "numeric",
19041
19685
  deviceType: DeviceType.Sensor,
19042
19686
  memberDeviceType: DeviceType.Sensor,
19687
+ memberDeviceTypes: [DeviceType.Sensor],
19688
+ activeWhen: void 0,
19043
19689
  commandCaps: [],
19044
19690
  baseUnit: baseUnitOf(input.quantity),
19045
19691
  aggregates: NUMERIC_AGGREGATES,
@@ -19055,6 +19701,8 @@ function binary(input) {
19055
19701
  family: "binary",
19056
19702
  deviceType: DeviceType.Sensor,
19057
19703
  memberDeviceType: DeviceType.Sensor,
19704
+ memberDeviceTypes: [DeviceType.Sensor],
19705
+ activeWhen: void 0,
19058
19706
  commandCaps: [],
19059
19707
  quantity: "none",
19060
19708
  baseUnit: void 0,
@@ -19068,73 +19716,73 @@ var GROUP_TEMPLATES = [
19068
19716
  summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
19069
19717
  role: DeviceRole.TemperatureSensor,
19070
19718
  outputCap: "temperature-sensor",
19071
- outputField: "celsius",
19719
+ outputField: "temperature",
19072
19720
  quantity: "temperature",
19073
19721
  defaultAggregate: "mean",
19074
19722
  empty: "null",
19075
- companions: [NOW]
19723
+ companions: [NOW$2]
19076
19724
  }),
19077
19725
  numeric({
19078
19726
  id: "humidity",
19079
19727
  summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
19080
19728
  role: DeviceRole.HumiditySensor,
19081
19729
  outputCap: "humidity-sensor",
19082
- outputField: "percent",
19730
+ outputField: "humidity",
19083
19731
  quantity: "humidity",
19084
19732
  defaultAggregate: "mean",
19085
19733
  empty: "null",
19086
- companions: [NOW]
19734
+ companions: [NOW$2]
19087
19735
  }),
19088
19736
  numeric({
19089
19737
  id: "pressure",
19090
19738
  summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
19091
19739
  role: DeviceRole.PressureSensor,
19092
19740
  outputCap: "pressure-sensor",
19093
- outputField: "hpa",
19741
+ outputField: "pressure",
19094
19742
  quantity: "pressure",
19095
19743
  defaultAggregate: "mean",
19096
19744
  empty: "null",
19097
- companions: [NOW]
19745
+ companions: [NOW$2]
19098
19746
  }),
19099
19747
  numeric({
19100
19748
  id: "illuminance",
19101
19749
  summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
19102
19750
  role: DeviceRole.AmbientLightSensor,
19103
19751
  outputCap: "ambient-light-sensor",
19104
- outputField: "lux",
19752
+ outputField: "illuminance",
19105
19753
  quantity: "illuminance",
19106
19754
  defaultAggregate: "mean",
19107
19755
  empty: "null",
19108
- companions: [NOW]
19756
+ companions: [NOW$2]
19109
19757
  }),
19110
19758
  numeric({
19111
19759
  id: "power",
19112
19760
  summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
19113
19761
  role: DeviceRole.PowerSensor,
19114
19762
  outputCap: "power-meter",
19115
- outputField: "watts",
19763
+ outputField: "power",
19116
19764
  quantity: "power",
19117
19765
  defaultAggregate: "sum",
19118
19766
  empty: "omit",
19119
- companions: [NOW]
19767
+ companions: [NOW$2]
19120
19768
  }),
19121
19769
  numeric({
19122
19770
  id: "energy",
19123
19771
  summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
19124
19772
  role: DeviceRole.EnergySensor,
19125
19773
  outputCap: "power-meter",
19126
- outputField: "kwhTotal",
19774
+ outputField: "energy",
19127
19775
  quantity: "energy",
19128
19776
  defaultAggregate: "sum",
19129
19777
  empty: "omit",
19130
- companions: [NOW]
19778
+ companions: [NOW$2]
19131
19779
  }),
19132
19780
  numeric({
19133
19781
  id: "battery",
19134
19782
  summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
19135
19783
  role: DeviceRole.BatterySensor,
19136
19784
  outputCap: "battery",
19137
- outputField: "percentage",
19785
+ outputField: "level",
19138
19786
  quantity: "percentage",
19139
19787
  defaultAggregate: "mean",
19140
19788
  empty: "null",
@@ -19162,7 +19810,7 @@ var GROUP_TEMPLATES = [
19162
19810
  quantity: "device",
19163
19811
  defaultAggregate: "mean",
19164
19812
  empty: "null",
19165
- companions: [NOW]
19813
+ companions: [NOW$2]
19166
19814
  }),
19167
19815
  binary({
19168
19816
  id: "motion",
@@ -19256,6 +19904,11 @@ var GROUP_TEMPLATES = [
19256
19904
  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.",
19257
19905
  deviceType: DeviceType.Switch,
19258
19906
  outputCap: "switch",
19907
+ outputField: "on",
19908
+ member: {
19909
+ cap: "switch",
19910
+ fieldPath: "on"
19911
+ },
19259
19912
  commandCaps: ["switch"]
19260
19913
  }),
19261
19914
  actuator({
@@ -19263,34 +19916,136 @@ var GROUP_TEMPLATES = [
19263
19916
  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.",
19264
19917
  deviceType: DeviceType.Light,
19265
19918
  outputCap: "switch",
19919
+ outputField: "on",
19920
+ member: {
19921
+ cap: "switch",
19922
+ fieldPath: "on"
19923
+ },
19266
19924
  commandCaps: [
19267
19925
  "switch",
19268
19926
  "brightness",
19269
19927
  "color"
19270
19928
  ]
19929
+ }),
19930
+ actuator({
19931
+ id: "fan",
19932
+ 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.",
19933
+ deviceType: DeviceType.Fan,
19934
+ outputCap: "switch",
19935
+ outputField: "on",
19936
+ member: {
19937
+ cap: "switch",
19938
+ fieldPath: "on"
19939
+ },
19940
+ commandCaps: ["switch", "fan-control"]
19941
+ }),
19942
+ actuator({
19943
+ id: "cover",
19944
+ 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.",
19945
+ deviceType: DeviceType.Cover,
19946
+ outputCap: "group-active",
19947
+ outputField: "active",
19948
+ member: {
19949
+ cap: "cover",
19950
+ fieldPath: "state"
19951
+ },
19952
+ activeWhen: [
19953
+ "open",
19954
+ "opening",
19955
+ "stopped"
19956
+ ],
19957
+ commandCaps: ["cover"]
19958
+ }),
19959
+ actuator({
19960
+ id: "valve",
19961
+ 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.",
19962
+ deviceType: DeviceType.Valve,
19963
+ outputCap: "group-active",
19964
+ outputField: "active",
19965
+ member: {
19966
+ cap: "valve",
19967
+ fieldPath: "state"
19968
+ },
19969
+ activeWhen: [
19970
+ "open",
19971
+ "opening",
19972
+ "stopped"
19973
+ ],
19974
+ commandCaps: ["valve"]
19975
+ }),
19976
+ actuator({
19977
+ id: "lock",
19978
+ 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.",
19979
+ deviceType: DeviceType.Lock,
19980
+ outputCap: "group-active",
19981
+ outputField: "active",
19982
+ member: {
19983
+ cap: "lock-control",
19984
+ fieldPath: "state"
19985
+ },
19986
+ activeWhen: ["locked"],
19987
+ commandCaps: ["lock-control"]
19988
+ }),
19989
+ actuator({
19990
+ id: "media-player",
19991
+ 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.",
19992
+ deviceType: DeviceType.MediaPlayer,
19993
+ outputCap: "group-active",
19994
+ outputField: "active",
19995
+ member: {
19996
+ cap: "media-player",
19997
+ fieldPath: "state"
19998
+ },
19999
+ activeWhen: ["playing", "buffering"],
20000
+ commandCaps: ["media-player"]
20001
+ }),
20002
+ actuator({
20003
+ id: "climate",
20004
+ 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.",
20005
+ deviceType: DeviceType.Thermostat,
20006
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
20007
+ outputCap: "group-active",
20008
+ outputField: "active",
20009
+ member: {
20010
+ cap: "climate-control",
20011
+ fieldPath: "mode"
20012
+ },
20013
+ activeWhen: [
20014
+ "heat",
20015
+ "cool",
20016
+ "auto",
20017
+ "heat_cool",
20018
+ "fan_only",
20019
+ "dry"
20020
+ ],
20021
+ commandCaps: ["climate-control"]
19271
20022
  })
19272
20023
  ];
19273
20024
  function actuator(input) {
19274
20025
  return {
19275
- ...input,
20026
+ id: input.id,
20027
+ summary: input.summary,
20028
+ deviceType: input.deviceType,
20029
+ outputCap: input.outputCap,
20030
+ outputField: input.outputField,
20031
+ commandCaps: input.commandCaps,
19276
20032
  family: "actuator",
19277
20033
  role: void 0,
19278
20034
  memberDeviceType: input.deviceType,
19279
- outputField: "on",
20035
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
20036
+ activeWhen: input.activeWhen,
19280
20037
  quantity: "none",
19281
20038
  baseUnit: void 0,
19282
20039
  aggregates: BINARY_AGGREGATES,
19283
20040
  defaultAggregate: "any",
19284
20041
  empty: "offline",
19285
- accept: [{
19286
- cap: "switch",
19287
- fieldPath: "on"
19288
- }],
20042
+ accept: [input.member],
19289
20043
  companions: [CHANGED]
19290
20044
  };
19291
20045
  }
19292
20046
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
19293
20047
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
20048
+ DeviceType.Camera;
19294
20049
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19295
20050
  /**
19296
20051
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -19605,7 +20360,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
19605
20360
  kind: "mutation",
19606
20361
  auth: "admin",
19607
20362
  access: "view"
19608
- });
20363
+ }), method(object({
20364
+ blockId: string().min(1),
20365
+ trace: AutomationRunTraceSchema.optional(),
20366
+ runStartedAt: number().optional(),
20367
+ keepTraces: number().int().min(1).max(100).optional()
20368
+ }), _void(), {
20369
+ kind: "mutation",
20370
+ auth: "admin"
20371
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
20372
+ traces: array(AutomationRunTraceSchema),
20373
+ lastError: string().nullable()
20374
+ }), { auth: "admin" });
19609
20375
  /**
19610
20376
  * `custom-model-registry` — collection cap exposing operator-registered
19611
20377
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -44514,7 +45280,7 @@ var NetworkAccessStatusSchema = object({
44514
45280
  });
44515
45281
  /**
44516
45282
  * Optional, richer endpoint shape returned by providers that expose
44517
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
45283
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44518
45284
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44519
45285
  * the originating provider config (mode + sourcePort) so the
44520
45286
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -44907,51 +45673,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
44907
45673
  kind: "mutation",
44908
45674
  auth: "admin"
44909
45675
  });
44910
- new Set([
44911
- {
44912
- id: "person",
44913
- name: "Person"
44914
- },
44915
- {
44916
- id: "vehicle",
44917
- name: "Vehicle"
44918
- },
44919
- {
44920
- id: "animal",
44921
- name: "Animal"
44922
- },
44923
- {
44924
- id: "package",
44925
- name: "Package"
44926
- }
44927
- ].map((l) => l.id));
44928
- var COCO_TO_MACRO = {
44929
- mapping: {
44930
- person: "person",
44931
- bicycle: "vehicle",
44932
- car: "vehicle",
44933
- motorcycle: "vehicle",
44934
- airplane: "vehicle",
44935
- bus: "vehicle",
44936
- train: "vehicle",
44937
- truck: "vehicle",
44938
- boat: "vehicle",
44939
- bird: "animal",
44940
- cat: "animal",
44941
- dog: "animal",
44942
- horse: "animal",
44943
- sheep: "animal",
44944
- cow: "animal",
44945
- elephant: "animal",
44946
- bear: "animal",
44947
- zebra: "animal",
44948
- giraffe: "animal",
44949
- suitcase: "package",
44950
- backpack: "package",
44951
- handbag: "package"
44952
- },
44953
- preserveOriginal: false
44954
- };
44955
45676
  var AUDIO_MACRO_LABELS = [
44956
45677
  {
44957
45678
  id: "speech",
@@ -47201,7 +47922,20 @@ var NcRuleInputSchema = object({
47201
47922
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47202
47923
  * path does not run Zod defaults.
47203
47924
  */
47204
- groupingSec: number().int().min(5).max(60).optional()
47925
+ groupingSec: number().int().min(5).max(60).optional(),
47926
+ /**
47927
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47928
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47929
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47930
+ * same mark the Library writes, and records whose notification it was and
47931
+ * when. A channel with no buttons gets the same two choices as links.
47932
+ *
47933
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47934
+ * cap path): absent and `false` both mean off, which is what every rule
47935
+ * authored before this field meant. An editor turns it off by writing
47936
+ * `false`, so it needs no `clear` key.
47937
+ */
47938
+ askVerdict: boolean().optional()
47205
47939
  });
47206
47940
  /**
47207
47941
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -57505,9 +58239,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
57505
58239
  */
57506
58240
  var AirQualitySensorStatusSchema = object({
57507
58241
  /** Carbon dioxide concentration in ppm. */
57508
- co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
58242
+ co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
57509
58243
  /** Total volatile organic compounds in ppb. */
57510
- vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
58244
+ voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57511
58245
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
57512
58246
  pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
57513
58247
  /** Particulate matter ≤ 10 μm in µg/m³. */
@@ -57540,12 +58274,12 @@ var airQualitySensorCapability = {
57540
58274
  * History (D850): recorded series.
57541
58275
  */
57542
58276
  history: { fields: {
57543
- co2Ppm: {
58277
+ co2: {
57544
58278
  kind: "gauge",
57545
58279
  unit: "ppm",
57546
58280
  deadband: 5
57547
58281
  },
57548
- vocPpb: {
58282
+ voc: {
57549
58283
  kind: "gauge",
57550
58284
  unit: "ppb",
57551
58285
  deadband: 5
@@ -57576,7 +58310,7 @@ var airQualitySensorCapability = {
57576
58310
  */
57577
58311
  var AmbientLightSensorStatusSchema = object({
57578
58312
  /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
57579
- lux: number().min(0).nullable().meta(readingMeta("illuminance", {
58313
+ illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
57580
58314
  precision: 0,
57581
58315
  label: "Illuminance"
57582
58316
  })),
@@ -57605,7 +58339,7 @@ var ambientLightSensorCapability = {
57605
58339
  /**
57606
58340
  * History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
57607
58341
  */
57608
- history: { fields: { lux: {
58342
+ history: { fields: { illuminance: {
57609
58343
  kind: "gauge",
57610
58344
  unit: "lx",
57611
58345
  deadbandPct: 10,
@@ -57744,6 +58478,84 @@ var audioMetricsCapability = {
57744
58478
  */
57745
58479
  history: "none"
57746
58480
  };
58481
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58482
+ actionId: string().min(1),
58483
+ variables: record(string(), ActionVariableValueSchema).optional()
58484
+ }), _void(), {
58485
+ kind: "mutation",
58486
+ auth: "admin"
58487
+ }), method(object({ actionId: string().min(1) }), _void(), {
58488
+ kind: "mutation",
58489
+ auth: "admin"
58490
+ });
58491
+ /**
58492
+ * actions — the ONE surface for runnable actions, for the UI and for
58493
+ * automations. Provided by the core-blocks builtin on the hub.
58494
+ *
58495
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
58496
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
58497
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
58498
+ * the run status from the hub's `runs` ledger; there is no catalog here to
58499
+ * drift (D3).
58500
+ *
58501
+ * **Permissions follow the camera, as linked devices do.** An action is linked
58502
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
58503
+ * non-admin may `get` / `run` an action only through a camera they may see that
58504
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
58505
+ * gate sees it) and the provider refuses an action that camera does not link.
58506
+ * An admin, and an in-process caller (an automation block), may run any action.
58507
+ * Every refusal is typed (`[not-linked] …`) and logged.
58508
+ *
58509
+ * A second `run` of an action that is already running is REFUSED — traced and
58510
+ * logged — never queued.
58511
+ */
58512
+ var ActionIdInput = object({ actionId: string().min(1) });
58513
+ var ActionLinksSchema = object({
58514
+ deviceId: number().int().nonnegative(),
58515
+ actionIds: array(string().min(1)).max(100)
58516
+ });
58517
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58518
+ actionId: string().min(1),
58519
+ deviceId: number().int().nonnegative().optional()
58520
+ }), object({ action: ActionEntrySchema.nullable() }), {
58521
+ role: "viewer",
58522
+ auth: "protected",
58523
+ kind: "query",
58524
+ caller: "required"
58525
+ }), method(object({
58526
+ actionId: string().min(1),
58527
+ variables: record(string(), ActionVariableValueSchema).optional(),
58528
+ wait: boolean().optional(),
58529
+ origin: ActionRunOriginSchema.optional(),
58530
+ deviceId: number().int().nonnegative().optional()
58531
+ }), ActionRunResultSchema, {
58532
+ role: "viewer",
58533
+ auth: "protected",
58534
+ kind: "mutation",
58535
+ caller: "required"
58536
+ }), method(ActionIdInput, _void(), {
58537
+ kind: "mutation",
58538
+ auth: "admin"
58539
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
58540
+ kind: "mutation",
58541
+ auth: "admin"
58542
+ }), method(object({
58543
+ actionId: string().min(1),
58544
+ action: ActionInputSchema.partial()
58545
+ }), object({ action: ActionEntrySchema }), {
58546
+ kind: "mutation",
58547
+ auth: "admin"
58548
+ }), method(ActionIdInput, _void(), {
58549
+ kind: "mutation",
58550
+ auth: "admin"
58551
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
58552
+ role: "viewer",
58553
+ auth: "protected",
58554
+ kind: "query"
58555
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
58556
+ kind: "mutation",
58557
+ auth: "admin"
58558
+ });
57747
58559
  /**
57748
58560
  * Automation-control cap. Models HA `automation.*` entities on
57749
58561
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -57840,7 +58652,7 @@ var BatteryStatusSchema = object({
57840
58652
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
57841
58653
  * declines to band a non-finite reading, which is the correct shape.
57842
58654
  */
57843
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
58655
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
57844
58656
  /**
57845
58657
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
57846
58658
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -57850,7 +58662,7 @@ var BatteryStatusSchema = object({
57850
58662
  * `'unknown'` is the absence of an answer: the provider has not read the
57851
58663
  * power fields yet, or the firmware reported neither. Before it existed,
57852
58664
  * two unreadable fields produced `'none'`, and a camera nobody had reached
57853
- * was drawn "on battery alone" — the same D315 defect `percentage` closed
58665
+ * was drawn "on battery alone" — the same D315 defect `level` closed
57854
58666
  * by going nullable. Consumers SKIP it (no charger state published, no
57855
58667
  * glyph, no HomeKit ChargingState) rather than coerce it either way.
57856
58668
  */
@@ -57874,7 +58686,7 @@ var BatteryStatusSchema = object({
57874
58686
  * that only report charge. A second reading on this cap, not a second
57875
58687
  * `numeric-sensor` on the same device.
57876
58688
  */
57877
- volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
58689
+ voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
57878
58690
  /**
57879
58691
  * Ms epoch of the last time the device PROVED it was reachable — a
57880
58692
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -57908,7 +58720,7 @@ var BatteryStatusSchema = object({
57908
58720
  /**
57909
58721
  * True when the source is a BINARY low-battery indicator (HA
57910
58722
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
57911
- * charge level — `percentage` is then a coarse stand-in (100 = normal,
58723
+ * charge level — `level` is then a coarse stand-in (100 = normal,
57912
58724
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
57913
58725
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
57914
58726
  */
@@ -57921,21 +58733,22 @@ var BATTERY_DEVICE_TYPES = [
57921
58733
  DeviceType.Switch,
57922
58734
  DeviceType.PetFeeder,
57923
58735
  DeviceType.Container,
57924
- DeviceType.Thermostat
58736
+ DeviceType.Thermostat,
58737
+ DeviceType.EventEmitter
57925
58738
  ];
57926
58739
  /** Generic Customize target for the percentage reading (D936). */
57927
58740
  var batteryCustomize = {
57928
58741
  version: 1,
57929
58742
  targets: [{
57930
- id: "percentage",
58743
+ id: "level",
57931
58744
  cardinality: "scalar",
57932
58745
  vocabulary: {
57933
58746
  cap: "battery",
57934
- stateField: "percentage"
58747
+ stateField: "level"
57935
58748
  },
57936
58749
  deviceTypes: BATTERY_DEVICE_TYPES,
57937
58750
  apply: "add",
57938
- path: "percentage",
58751
+ path: "level",
57939
58752
  sources: [{
57940
58753
  kind: "field",
57941
58754
  valueKinds: ["number", "string"],
@@ -58029,7 +58842,7 @@ onStatusChanged: { data: object({
58029
58842
  schema: BatteryStatusSchema,
58030
58843
  kind: "push",
58031
58844
  empty: {
58032
- percentage: null,
58845
+ level: null,
58033
58846
  charging: "unknown",
58034
58847
  sleeping: false,
58035
58848
  lastUpdated: 0
@@ -58056,11 +58869,11 @@ onStatusChanged: { data: object({
58056
58869
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
58057
58870
  */
58058
58871
  history: { fields: {
58059
- percentage: {
58872
+ level: {
58060
58873
  kind: "gauge",
58061
58874
  unit: "%"
58062
58875
  },
58063
- volts: {
58876
+ voltage: {
58064
58877
  kind: "gauge",
58065
58878
  unit: "V"
58066
58879
  }
@@ -58120,14 +58933,14 @@ var binaryCapability = {
58120
58933
  * controls to this single-method surface.
58121
58934
  *
58122
58935
  * The cap is intentionally minimal: a single `setBrightness({deviceId,
58123
- * percentage})` mutation plus the auto-injected `getStatus`. Drivers
58936
+ * level})` mutation plus the auto-injected `getStatus`. Drivers
58124
58937
  * that expose richer controls (color temperature, scenes, schedules)
58125
58938
  * should surface those via the device's `getSettingsUISchema()`
58126
58939
  * instead of bloating this cap.
58127
58940
  */
58128
58941
  var BrightnessStatusSchema = object({
58129
58942
  /** Current level as 0..100 inclusive. Firmware-reported. */
58130
- percentage: number().min(0).max(100).meta(readingMeta("percentage")),
58943
+ level: number().min(0).max(100).meta(readingMeta("percentage")),
58131
58944
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
58132
58945
  lastChangedAt: number()
58133
58946
  });
@@ -58139,7 +58952,7 @@ var brightnessCapability = {
58139
58952
  deviceTypes: [DeviceType.Light],
58140
58953
  methods: { setBrightness: method(object({
58141
58954
  deviceId: number().int().nonnegative(),
58142
- percentage: number().min(0).max(100)
58955
+ level: number().min(0).max(100)
58143
58956
  }), _void(), {
58144
58957
  kind: "mutation",
58145
58958
  auth: "protected"
@@ -58152,7 +58965,7 @@ var brightnessCapability = {
58152
58965
  */
58153
58966
  onBrightnessChanged: { data: object({
58154
58967
  deviceId: number(),
58155
- percentage: number().min(0).max(100),
58968
+ level: number().min(0).max(100),
58156
58969
  lastChangedAt: number()
58157
58970
  }) } },
58158
58971
  status: {
@@ -58175,7 +58988,7 @@ onBrightnessChanged: { data: object({
58175
58988
  /**
58176
58989
  * History (D850): recorded series. Actuator tier: level keeps every change.
58177
58990
  */
58178
- history: { fields: { percentage: {
58991
+ history: { fields: { level: {
58179
58992
  kind: "gauge",
58180
58993
  unit: "%",
58181
58994
  minIntervalMs: 0
@@ -58772,11 +59585,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
58772
59585
  }),
58773
59586
  object({
58774
59587
  mode: literal("mired"),
58775
- mireds: number().int().min(50).max(1e3)
59588
+ colorTemperature: number().int().min(50).max(1e3)
58776
59589
  })
58777
59590
  ]);
58778
59591
  var ColorStatusSchema = object({
58779
- /** Active color mode — which of `rgb` / `hsv` / `mireds` reflects
59592
+ /** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
58780
59593
  * the bulb's current state. */
58781
59594
  mode: _enum([
58782
59595
  "rgb",
@@ -58788,7 +59601,7 @@ var ColorStatusSchema = object({
58788
59601
  /** Populated when `mode === 'hsv'`. */
58789
59602
  hsv: HsvTripletSchema.optional(),
58790
59603
  /** Populated when `mode === 'mired'`. */
58791
- mireds: number().int().optional().meta(readingMeta("colour-temperature")),
59604
+ colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
58792
59605
  /** Ms epoch of the last operator-driven change. */
58793
59606
  lastChangedAt: number()
58794
59607
  });
@@ -58820,7 +59633,7 @@ onColorChanged: { data: object({
58820
59633
  ]),
58821
59634
  rgb: RgbTripletSchema.optional(),
58822
59635
  hsv: HsvTripletSchema.optional(),
58823
- mireds: number().int().optional(),
59636
+ colorTemperature: number().int().optional(),
58824
59637
  lastChangedAt: number()
58825
59638
  }) } },
58826
59639
  status: {
@@ -59705,11 +60518,11 @@ var DishwasherStatusSchema = object({
59705
60518
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
59706
60519
  availablePrograms: array(string()),
59707
60520
  /** Seconds remaining on the active cycle, or null when not running / not known. */
59708
- remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60521
+ remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59709
60522
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
59710
- delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60523
+ delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59711
60524
  /** Cycle completion 0..100, or null when not applicable. */
59712
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60525
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59713
60526
  /** True when the door is reported open; null when the door state is not known. */
59714
60527
  doorOpen: boolean().nullable(),
59715
60528
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -59928,14 +60741,14 @@ var dishwasherCapability = {
59928
60741
  runtimeState: DishwasherStatusSchema,
59929
60742
  durability: "session",
59930
60743
  history: { fields: {
59931
- progressPercent: {
60744
+ progress: {
59932
60745
  kind: "gauge",
59933
60746
  unit: "%",
59934
60747
  deadband: 2
59935
60748
  },
59936
60749
  state: { kind: "enum" },
59937
60750
  operationState: { kind: "enum" },
59938
- remainingSeconds: {
60751
+ remaining: {
59939
60752
  kind: "gauge",
59940
60753
  unit: "s",
59941
60754
  minIntervalMs: 6e4
@@ -60526,7 +61339,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
60526
61339
  var FanControlStatusSchema = object({
60527
61340
  /** Active speed as 0..100 inclusive. Null when the device has no
60528
61341
  * speed surface (single-speed fan). */
60529
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61342
+ speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60530
61343
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
60531
61344
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
60532
61345
  percentageStep: number().positive().optional().meta(readingMeta("percentage")),
@@ -60553,7 +61366,7 @@ var fanControlCapability = {
60553
61366
  methods: {
60554
61367
  setPercentage: method(object({
60555
61368
  deviceId: number().int().nonnegative(),
60556
- percentage: number().min(0).max(100)
61369
+ speed: number().min(0).max(100)
60557
61370
  }), _void(), {
60558
61371
  kind: "mutation",
60559
61372
  auth: "protected"
@@ -60586,7 +61399,7 @@ var fanControlCapability = {
60586
61399
  },
60587
61400
  /**
60588
61401
  * Runtime-state slice — mirrored by the kernel. UI fan speed
60589
- * sliders read `percentage` for live updates.
61402
+ * sliders read `speed` for live updates.
60590
61403
  */
60591
61404
  runtimeState: FanControlStatusSchema,
60592
61405
  /**
@@ -60600,7 +61413,7 @@ var fanControlCapability = {
60600
61413
  * History (D850): recorded series. Actuator tier: speed keeps every change.
60601
61414
  */
60602
61415
  history: { fields: {
60603
- percentage: {
61416
+ speed: {
60604
61417
  kind: "gauge",
60605
61418
  unit: "%",
60606
61419
  minIntervalMs: 0
@@ -60789,7 +61602,17 @@ var groupCommandsCapability = {
60789
61602
  scope: "device",
60790
61603
  deviceNative: true,
60791
61604
  mode: "singleton",
60792
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
61605
+ deviceTypes: [
61606
+ DeviceType.Switch,
61607
+ DeviceType.Light,
61608
+ DeviceType.Fan,
61609
+ DeviceType.Cover,
61610
+ DeviceType.Valve,
61611
+ DeviceType.Lock,
61612
+ DeviceType.MediaPlayer,
61613
+ DeviceType.Thermostat,
61614
+ DeviceType.Climate
61615
+ ],
60793
61616
  methods: {},
60794
61617
  status: {
60795
61618
  schema: GroupCommandsSchema,
@@ -60807,6 +61630,49 @@ var groupCommandsCapability = {
60807
61630
  history: "none"
60808
61631
  };
60809
61632
  /**
61633
+ * Whether a helper actuator group counts as active (D944): at least one
61634
+ * included member is, or every included member is. What "active" means is the
61635
+ * type's: a cover is open, a lock is locked, a media player is playing, a
61636
+ * climate device is not off. A group of lights, switches and fans reports
61637
+ * `switch.on` instead; this cap is for the types whose own state is not a
61638
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
61639
+ * empty included set takes the device offline, never `active: false`.
61640
+ */
61641
+ var GroupActiveSchema = object({
61642
+ active: boolean().meta({ label: "Active" }),
61643
+ /** Ms epoch when the slice was last updated. */
61644
+ lastChangedAt: number()
61645
+ });
61646
+ var groupActiveCapability = {
61647
+ name: "group-active",
61648
+ scope: "device",
61649
+ deviceNative: true,
61650
+ mode: "singleton",
61651
+ deviceTypes: [
61652
+ DeviceType.Cover,
61653
+ DeviceType.Valve,
61654
+ DeviceType.Lock,
61655
+ DeviceType.MediaPlayer,
61656
+ DeviceType.Climate,
61657
+ DeviceType.Thermostat
61658
+ ],
61659
+ methods: {},
61660
+ status: {
61661
+ schema: GroupActiveSchema,
61662
+ kind: "push"
61663
+ },
61664
+ runtimeState: GroupActiveSchema,
61665
+ /**
61666
+ * Runtime-state durability: **session** — recomputed from the members on the
61667
+ * next source event; a restored value would outlive the members it named.
61668
+ *
61669
+ * See `RuntimeStateDurability`. Enforced by
61670
+ * `scripts/check-runtime-state-durability.ts`.
61671
+ */
61672
+ durability: "session",
61673
+ history: "none"
61674
+ };
61675
+ /**
60810
61676
  * How many members of a helper group answered (D944). Counts are integers,
60811
61677
  * not measurements: zero included is a real count, never a stand-in reading.
60812
61678
  * The reading itself stays on the output capability. History is none — the
@@ -60837,7 +61703,14 @@ var groupStatusCapability = {
60837
61703
  deviceTypes: [
60838
61704
  DeviceType.Sensor,
60839
61705
  DeviceType.Switch,
60840
- DeviceType.Light
61706
+ DeviceType.Light,
61707
+ DeviceType.Fan,
61708
+ DeviceType.Cover,
61709
+ DeviceType.Valve,
61710
+ DeviceType.Lock,
61711
+ DeviceType.MediaPlayer,
61712
+ DeviceType.Thermostat,
61713
+ DeviceType.Climate
60841
61714
  ],
60842
61715
  methods: {},
60843
61716
  status: {
@@ -60985,7 +61858,7 @@ var humidifierCapability = {
60985
61858
  */
60986
61859
  var HumiditySensorStatusSchema = object({
60987
61860
  /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
60988
- percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
61861
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
60989
61862
  precision: 0,
60990
61863
  label: "Humidity"
60991
61864
  })),
@@ -61014,7 +61887,7 @@ var humiditySensorCapability = {
61014
61887
  /**
61015
61888
  * History (D850): recorded series.
61016
61889
  */
61017
- history: { fields: { percent: {
61890
+ history: { fields: { humidity: {
61018
61891
  kind: "gauge",
61019
61892
  unit: "%",
61020
61893
  deadband: .5
@@ -61654,7 +62527,7 @@ var LawnMowerControlStatusSchema = object({
61654
62527
  batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61655
62528
  /** 0..100 mowing-completion percentage of the current task, or null when no
61656
62529
  * task is active / progress is unavailable. */
61657
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62530
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61658
62531
  /** Current device/event code (dynamic — mostly status, sometimes an error),
61659
62532
  * or null when unknown. */
61660
62533
  currentCode: number().nullable().meta(readingMeta("none")),
@@ -62110,7 +62983,14 @@ var lockControlCapability = {
62110
62983
  }),
62111
62984
  unlock: method(object({
62112
62985
  deviceId: number().int().nonnegative(),
62113
- code: string().min(1).optional()
62986
+ code: string().min(1).optional(),
62987
+ /**
62988
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62989
+ * with one call is refused unless the caller says it was confirmed. A
62990
+ * single lock ignores it. The refusal lives in the callee, so no
62991
+ * automation or API caller reaches the doors by skipping a dialog.
62992
+ */
62993
+ confirm: boolean().optional()
62114
62994
  }), _void(), {
62115
62995
  kind: "mutation",
62116
62996
  auth: "protected"
@@ -62147,6 +63027,44 @@ var lockControlCapability = {
62147
63027
  volatileStateFields: ["lastChangedAt"]
62148
63028
  };
62149
63029
  /**
63030
+ * A monotonic total between resets (D944). Energy uses `power-meter.energy`
63031
+ * so dashboards keep one series; every other instantaneous reading lands here.
63032
+ * `value` is the time integral in the source quantity's accumulated base
63033
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
63034
+ */
63035
+ var MeterStatusSchema = object({
63036
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
63037
+ lastFetchedAt: number()
63038
+ });
63039
+ var meterCapability = {
63040
+ name: "meter",
63041
+ scope: "device",
63042
+ deviceNative: true,
63043
+ mode: "singleton",
63044
+ deviceTypes: [DeviceType.Sensor],
63045
+ methods: {},
63046
+ status: {
63047
+ schema: MeterStatusSchema,
63048
+ kind: "push"
63049
+ },
63050
+ runtimeState: MeterStatusSchema,
63051
+ /**
63052
+ * Runtime-state durability: **restored** — the total is a cache of the
63053
+ * history integral. A restart shows it only until the history pass
63054
+ * recomputes the open period; it is not an in-memory buffer.
63055
+ *
63056
+ * See `RuntimeStateDurability`. Enforced by
63057
+ * `scripts/check-runtime-state-durability.ts`.
63058
+ */
63059
+ durability: "restored",
63060
+ history: { fields: { value: {
63061
+ kind: "counter",
63062
+ deadband: .001,
63063
+ minIntervalMs: 6e4
63064
+ } } },
63065
+ volatileStateFields: ["lastFetchedAt"]
63066
+ };
63067
+ /**
62150
63068
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
62151
63069
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
62152
63070
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -62203,10 +63121,10 @@ var MediaPlayerStatusSchema = object({
62203
63121
  currentMedia: MediaInfoSchema.nullable(),
62204
63122
  /** Current playback position in ms. Null when not seekable or
62205
63123
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
62206
- positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
63124
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62207
63125
  /** Total duration of the current media in ms. Null when unknown
62208
63126
  * (live stream) or nothing is playing. */
62209
- durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
63127
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62210
63128
  /** Shuffle toggle. Null when no shuffle surface.
62211
63129
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
62212
63130
  shuffle: boolean().nullable(),
@@ -62245,7 +63163,7 @@ var mediaPlayerCapability = {
62245
63163
  }),
62246
63164
  seek: method(object({
62247
63165
  deviceId: number().int().nonnegative(),
62248
- positionMs: number().int().nonnegative()
63166
+ position: number().int().nonnegative()
62249
63167
  }), _void(), {
62250
63168
  kind: "mutation",
62251
63169
  auth: "protected"
@@ -62317,7 +63235,7 @@ var mediaPlayerCapability = {
62317
63235
  /**
62318
63236
  * History (D850): volume (a level an operator or an automation sets; the
62319
63237
  * default 10 s floor folds a slider drag into its end value) and mute.
62320
- * `positionMs` is not recorded: it moves continuously while playing.
63238
+ * `position` is not recorded: it moves continuously while playing.
62321
63239
  */
62322
63240
  history: { fields: {
62323
63241
  volumeLevel: {
@@ -63279,9 +64197,9 @@ var NetworkLinkStatusSchema = object({
63279
64197
  * one whose reading has not landed must not be drawn at 0 %. Consumers
63280
64198
  * SKIP a null rather than coerce it.
63281
64199
  */
63282
- signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64200
+ signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63283
64201
  /** Raw received signal strength in dBm, when the firmware reports one. */
63284
- rssiDbm: number().optional().meta(readingMeta("signal-level")),
64202
+ rssi: number().optional().meta(readingMeta("signal-level")),
63285
64203
  /** Network name of a wireless link, when the firmware reports it. */
63286
64204
  ssid: string().optional(),
63287
64205
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
@@ -63317,7 +64235,7 @@ onStatusChanged: { data: object({
63317
64235
  kind: "push",
63318
64236
  empty: {
63319
64237
  type: "unknown",
63320
- signalPercent: null,
64238
+ signal: null,
63321
64239
  lastUpdated: 0
63322
64240
  }
63323
64241
  },
@@ -63340,13 +64258,13 @@ onStatusChanged: { data: object({
63340
64258
  * History (D850): recorded series.
63341
64259
  */
63342
64260
  history: { fields: {
63343
- signalPercent: {
64261
+ signal: {
63344
64262
  kind: "gauge",
63345
64263
  unit: "%",
63346
64264
  deadband: 3,
63347
64265
  minIntervalMs: 6e4
63348
64266
  },
63349
- rssiDbm: {
64267
+ rssi: {
63350
64268
  kind: "gauge",
63351
64269
  unit: "dBm",
63352
64270
  deadband: 2,
@@ -64091,10 +65009,20 @@ var PetFeederDeviceStatusSchema = _enum([
64091
65009
  ]);
64092
65010
  var gramsPortion = number().int().min(4).max(200);
64093
65011
  var PetFeederStatusSchema = object({
64094
- /** Food currently in the bowl (grams). Null when the device has not
64095
- * reported a reading yet. On dual-hopper models this is the combined
64096
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
65012
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
65013
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
65014
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
65015
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
65016
+ * the device has not reported yet. */
64097
65017
  foodLevel: number().nullable().meta(readingMeta("none")),
65018
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
65019
+ * feeder's own units. Null when the model reports none. */
65020
+ bowlFood: number().nullable().meta(readingMeta("none")),
65021
+ /** True while the pet is eating (`state.eating`). */
65022
+ eating: boolean(),
65023
+ /** Whether `markFoodReplenished` does anything on this model. When false
65024
+ * the client must not offer it. */
65025
+ supportsMarkRefilled: boolean(),
64098
65026
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
64099
65027
  * single-hopper models. */
64100
65028
  food1: number().nullable().meta(readingMeta("none")),
@@ -64102,8 +65030,8 @@ var PetFeederStatusSchema = object({
64102
65030
  * single-hopper models. */
64103
65031
  food2: number().nullable().meta(readingMeta("none")),
64104
65032
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
64105
- * (`device_class: problem`, on = low). True when the bowl is empty /
64106
- * below the feeder's low threshold. */
65033
+ * (`device_class: problem`, on = low). True when the tank reports empty,
65034
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
64107
65035
  lowFood: boolean(),
64108
65036
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
64109
65037
  * device has no battery reading. */
@@ -64171,7 +65099,9 @@ var petFeederCapability = {
64171
65099
  kind: "mutation",
64172
65100
  auth: "protected"
64173
65101
  }),
64174
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
65102
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
65103
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
65104
+ * change any sensor reading. */
64175
65105
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
64176
65106
  kind: "mutation",
64177
65107
  auth: "protected"
@@ -64244,6 +65174,7 @@ var petFeederCapability = {
64244
65174
  */
64245
65175
  history: { fields: {
64246
65176
  foodLevel: { kind: "gauge" },
65177
+ bowlFood: { kind: "gauge" },
64247
65178
  food1: { kind: "gauge" },
64248
65179
  food2: { kind: "gauge" },
64249
65180
  batteryPower: {
@@ -64653,27 +65584,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
64653
65584
  * in real-world devices.
64654
65585
  */
64655
65586
  var PowerMeterStatusSchema = object({
64656
- /** Instantaneous power draw in watts. */
64657
- watts: number().optional().meta(readingMeta("power", { label: "Power" })),
65587
+ /** Instantaneous power draw (watts). */
65588
+ power: number().optional().meta(readingMeta("power", { label: "Power" })),
64658
65589
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64659
- kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64660
- /** Voltage in volts. */
64661
- volts: number().optional().meta(readingMeta("voltage")),
65590
+ energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
65591
+ /** Voltage (volts). */
65592
+ voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64662
65593
  /** Current in amperes. */
64663
- amps: number().optional().meta(readingMeta("current")),
65594
+ current: number().optional().meta(readingMeta("current", { label: "Current" })),
64664
65595
  /** Apparent power in volt-amperes. */
64665
- va: number().optional().meta(readingMeta("apparent-power")),
65596
+ apparentPower: number().optional().meta(readingMeta("apparent-power")),
64666
65597
  /** Power factor (0..1) if reported. */
64667
65598
  powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
64668
65599
  /** Ms epoch when the slice was last updated. */
64669
65600
  lastFetchedAt: number()
64670
65601
  });
65602
+ /**
65603
+ * Loads that draw metered mains power (D951). The reading is a feature of the
65604
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
65605
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
65606
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
65607
+ * camera and the like are left out: their draw is not what anyone meters.
65608
+ */
65609
+ var POWER_METER_DEVICE_TYPES = [
65610
+ DeviceType.Sensor,
65611
+ DeviceType.Switch,
65612
+ DeviceType.Light,
65613
+ DeviceType.Fan,
65614
+ DeviceType.Climate,
65615
+ DeviceType.Thermostat,
65616
+ DeviceType.Humidifier,
65617
+ DeviceType.WaterHeater,
65618
+ DeviceType.MediaPlayer,
65619
+ DeviceType.Washer,
65620
+ DeviceType.Dryer,
65621
+ DeviceType.Dishwasher,
65622
+ DeviceType.Vacuum,
65623
+ DeviceType.LawnMower,
65624
+ DeviceType.PetFeeder
65625
+ ];
65626
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
65627
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
65628
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
65629
+ function powerTarget(id, unit) {
65630
+ return {
65631
+ id,
65632
+ cardinality: "scalar",
65633
+ vocabulary: {
65634
+ cap: "power-meter",
65635
+ stateField: id
65636
+ },
65637
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
65638
+ apply: "add",
65639
+ path: id,
65640
+ sources: [{
65641
+ kind: "numeric",
65642
+ requiredUnit: unit,
65643
+ transforms: [{ kind: "identity" }]
65644
+ }],
65645
+ companions: [{
65646
+ kind: "clock",
65647
+ path: "lastFetchedAt",
65648
+ when: "always"
65649
+ }],
65650
+ sourceReport: { status: "assignment-status" },
65651
+ advancedExpression: true
65652
+ };
65653
+ }
65654
+ /** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
65655
+ var powerMeterCustomize = {
65656
+ version: 1,
65657
+ targets: [
65658
+ powerTarget("power", "W"),
65659
+ powerTarget("energy", "kWh"),
65660
+ powerTarget("voltage", "V"),
65661
+ powerTarget("current", "A")
65662
+ ]
65663
+ };
64671
65664
  var powerMeterCapability = {
64672
65665
  name: "power-meter",
64673
65666
  scope: "device",
64674
65667
  deviceNative: true,
64675
65668
  mode: "singleton",
64676
- deviceTypes: [DeviceType.Sensor],
65669
+ deviceTypes: POWER_METER_DEVICE_TYPES,
64677
65670
  methods: {},
64678
65671
  status: {
64679
65672
  schema: PowerMeterStatusSchema,
@@ -64681,41 +65674,41 @@ var powerMeterCapability = {
64681
65674
  },
64682
65675
  runtimeState: PowerMeterStatusSchema,
64683
65676
  /**
64684
- * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
65677
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
64685
65678
  *
64686
65679
  * See `RuntimeStateDurability`. Enforced by
64687
65680
  * `scripts/check-runtime-state-durability.ts`.
64688
65681
  */
64689
65682
  durability: "restored",
64690
65683
  /**
64691
- * History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
65684
+ * History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
64692
65685
  */
64693
65686
  history: { fields: {
64694
- watts: {
65687
+ power: {
64695
65688
  kind: "gauge",
64696
65689
  unit: "W",
64697
65690
  deadband: 1,
64698
65691
  minIntervalMs: 3e4
64699
65692
  },
64700
- kwhTotal: {
65693
+ energy: {
64701
65694
  kind: "counter",
64702
65695
  unit: "kWh",
64703
65696
  deadband: .01,
64704
65697
  minIntervalMs: 6e4
64705
65698
  },
64706
- volts: {
65699
+ voltage: {
64707
65700
  kind: "gauge",
64708
65701
  unit: "V",
64709
65702
  deadband: 1,
64710
65703
  minIntervalMs: 3e4
64711
65704
  },
64712
- amps: {
65705
+ current: {
64713
65706
  kind: "gauge",
64714
65707
  unit: "A",
64715
65708
  deadband: .05,
64716
65709
  minIntervalMs: 3e4
64717
65710
  },
64718
- va: {
65711
+ apparentPower: {
64719
65712
  kind: "gauge",
64720
65713
  unit: "VA",
64721
65714
  deadband: 1,
@@ -64730,7 +65723,8 @@ var powerMeterCapability = {
64730
65723
  } },
64731
65724
  /** Clock fields: written, but excluded from the compare that decides
64732
65725
  * whether persisting is worth a SQLite commit. */
64733
- volatileStateFields: ["lastFetchedAt"]
65726
+ volatileStateFields: ["lastFetchedAt"],
65727
+ customize: powerMeterCustomize
64734
65728
  };
64735
65729
  /**
64736
65730
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -64764,9 +65758,6 @@ var PresenceStatusSchema = object({
64764
65758
  location: string().nullable(),
64765
65759
  /** GPS coordinates when available. Null otherwise. */
64766
65760
  gps: GpsLocationSchema.nullable(),
64767
- /** Optional battery level of the tracking device (0..100). Null
64768
- * when not reported. */
64769
- batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64770
65761
  /** Ms epoch when the slice was last updated. */
64771
65762
  lastChangedAt: number()
64772
65763
  });
@@ -64795,14 +65786,7 @@ var presenceCapability = {
64795
65786
  * `scripts/check-runtime-state-durability.ts`.
64796
65787
  */
64797
65788
  durability: "restored",
64798
- /**
64799
- * History (D850): recorded series.
64800
- */
64801
- history: { fields: { batteryPercent: {
64802
- kind: "gauge",
64803
- unit: "%",
64804
- deadband: 1
64805
- } } },
65789
+ history: "none",
64806
65790
  /** Clock fields: written, but excluded from the compare that decides
64807
65791
  * whether persisting is worth a SQLite commit. */
64808
65792
  volatileStateFields: ["lastChangedAt"]
@@ -64817,7 +65801,7 @@ var presenceCapability = {
64817
65801
  */
64818
65802
  var PressureSensorStatusSchema = object({
64819
65803
  /** Current pressure in hPa. `null` when the sample was refused (D393). */
64820
- hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
65804
+ pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
64821
65805
  /** Ms epoch when the slice was last updated. */
64822
65806
  lastFetchedAt: number()
64823
65807
  });
@@ -64843,7 +65827,7 @@ var pressureSensorCapability = {
64843
65827
  /**
64844
65828
  * History (D850): recorded series.
64845
65829
  */
64846
- history: { fields: { hpa: {
65830
+ history: { fields: { pressure: {
64847
65831
  kind: "gauge",
64848
65832
  unit: "hPa",
64849
65833
  deadband: .1
@@ -65411,6 +66395,8 @@ var runningCustomize = {
65411
66395
  stateField: "running"
65412
66396
  },
65413
66397
  deviceTypes: [
66398
+ DeviceType.Washer,
66399
+ DeviceType.Dryer,
65414
66400
  DeviceType.Dishwasher,
65415
66401
  DeviceType.Vacuum,
65416
66402
  DeviceType.LawnMower,
@@ -67900,7 +68886,8 @@ var switchCapability = {
67900
68886
  deviceTypes: [
67901
68887
  DeviceType.Switch,
67902
68888
  DeviceType.Siren,
67903
- DeviceType.Light
68889
+ DeviceType.Light,
68890
+ DeviceType.Fan
67904
68891
  ],
67905
68892
  methods: { setState: method(object({
67906
68893
  deviceId: number(),
@@ -68539,7 +69526,7 @@ var tamperCapability = {
68539
69526
  */
68540
69527
  var TemperatureSensorStatusSchema = object({
68541
69528
  /** Current temperature in Celsius. `null` when the sample was refused (D393). */
68542
- celsius: number().nullable().meta(readingMeta("temperature", {
69529
+ temperature: number().nullable().meta(readingMeta("temperature", {
68543
69530
  precision: 1,
68544
69531
  label: "Temperature"
68545
69532
  })),
@@ -68568,7 +69555,7 @@ var temperatureSensorCapability = {
68568
69555
  /**
68569
69556
  * History (D850): recorded series.
68570
69557
  */
68571
- history: { fields: { celsius: {
69558
+ history: { fields: { temperature: {
68572
69559
  kind: "gauge",
68573
69560
  unit: "°C",
68574
69561
  deadband: .1
@@ -68865,7 +69852,7 @@ var VacuumControlStatusSchema = object({
68865
69852
  /** Speed tokens the hardware accepts — drives the UI selector. */
68866
69853
  availableFanSpeeds: array(string()),
68867
69854
  /** 0..100 cleaning-completion percentage of the current task, or null. */
68868
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
69855
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
68869
69856
  /** Current error code (0 / null = no error). */
68870
69857
  errorCode: number().nullable().meta(readingMeta("none")),
68871
69858
  /** Human label for {@link errorCode}, or null when none / undecodable. */
@@ -70113,8 +71100,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
70113
71100
  kind: "mutation",
70114
71101
  auth: "admin"
70115
71102
  });
71103
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
70116
71104
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
70117
- 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;
71105
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, 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.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
70118
71106
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
70119
71107
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
70120
71108
  new Set(CAP_NAMES);
@@ -70443,6 +71431,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70443
71431
  featureProbe: featureProbeCapability,
70444
71432
  flood: floodCapability,
70445
71433
  gas: gasCapability,
71434
+ groupActive: groupActiveCapability,
70446
71435
  groupCommands: groupCommandsCapability,
70447
71436
  groupStatus: groupStatusCapability,
70448
71437
  heatingValve: heatingValveCapability,
@@ -70454,6 +71443,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70454
71443
  lawnMowerControl: lawnMowerControlCapability,
70455
71444
  lockControl: lockControlCapability,
70456
71445
  mediaPlayer: mediaPlayerCapability,
71446
+ meter: meterCapability,
70457
71447
  motion: motionCapability,
70458
71448
  motionTrigger: motionTriggerCapability,
70459
71449
  motionZones: motionZonesCapability,
@@ -70516,7 +71506,7 @@ var BaseDevice = class {
70516
71506
  * `device-local-state.ts`. Drivers access via:
70517
71507
  *
70518
71508
  * `this.state.battery.sleeping = true` // patches the battery slice
70519
- * `const pct = this.state.battery.percentage` // reads the battery slice
71509
+ * `const pct = this.state.battery.level` // reads the battery slice
70520
71510
  * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
70521
71511
  *
70522
71512
  * Adding a new cap with `runtimeState:` automatically extends this
@@ -70556,7 +71546,7 @@ var BaseDevice = class {
70556
71546
  * shape; drivers don't redeclare it, they just write through.
70557
71547
  *
70558
71548
  * Read: `this.runtimeState.getCapState('battery')` →
70559
- * `{percentage, charging, sleeping, lastUpdated}` for any
71549
+ * `{level, charging, sleeping, lastUpdated}` for any
70560
71550
  * provider that registers `batteryCapability`.
70561
71551
  * Write: `this.runtimeState.setCapState('battery', { … })`.
70562
71552
  *
@@ -71713,7 +72703,7 @@ var BaseDeviceProvider = class extends BaseAddon {
71713
72703
  return toDeviceSummary(device, this.addonId);
71714
72704
  }
71715
72705
  };
71716
- 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;
72706
+ 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;
71717
72707
  new Set(Object.values(DeviceType));
71718
72708
  Object.freeze({
71719
72709
  "accessories.getStatus": {
@@ -71730,6 +72720,104 @@ Object.freeze({
71730
72720
  access: "create",
71731
72721
  role: "admin"
71732
72722
  },
72723
+ "actions.create": {
72724
+ capName: "actions",
72725
+ capScope: "system",
72726
+ addonId: null,
72727
+ access: "create",
72728
+ role: "admin"
72729
+ },
72730
+ "actions.delete": {
72731
+ capName: "actions",
72732
+ capScope: "system",
72733
+ addonId: null,
72734
+ access: "delete",
72735
+ role: "admin"
72736
+ },
72737
+ "actions.get": {
72738
+ capName: "actions",
72739
+ capScope: "system",
72740
+ addonId: null,
72741
+ access: "view",
72742
+ role: "viewer"
72743
+ },
72744
+ "actions.getLinks": {
72745
+ capName: "actions",
72746
+ capScope: "system",
72747
+ addonId: null,
72748
+ access: "view",
72749
+ role: "admin"
72750
+ },
72751
+ "actions.getTraces": {
72752
+ capName: "actions",
72753
+ capScope: "system",
72754
+ addonId: null,
72755
+ access: "view",
72756
+ role: "admin"
72757
+ },
72758
+ "actions.list": {
72759
+ capName: "actions",
72760
+ capScope: "system",
72761
+ addonId: null,
72762
+ access: "view",
72763
+ role: "admin"
72764
+ },
72765
+ "actions.listForCamera": {
72766
+ capName: "actions",
72767
+ capScope: "system",
72768
+ addonId: null,
72769
+ access: "view",
72770
+ role: "viewer"
72771
+ },
72772
+ "actions.run": {
72773
+ capName: "actions",
72774
+ capScope: "system",
72775
+ addonId: null,
72776
+ access: "create",
72777
+ role: "viewer"
72778
+ },
72779
+ "actions.setLinks": {
72780
+ capName: "actions",
72781
+ capScope: "system",
72782
+ addonId: null,
72783
+ access: "create",
72784
+ role: "admin"
72785
+ },
72786
+ "actions.stop": {
72787
+ capName: "actions",
72788
+ capScope: "system",
72789
+ addonId: null,
72790
+ access: "create",
72791
+ role: "admin"
72792
+ },
72793
+ "actions.update": {
72794
+ capName: "actions",
72795
+ capScope: "system",
72796
+ addonId: null,
72797
+ access: "create",
72798
+ role: "admin"
72799
+ },
72800
+ "actionSource.listActions": {
72801
+ capName: "action-source",
72802
+ capScope: "system",
72803
+ addonId: null,
72804
+ access: "view",
72805
+ role: "admin"
72806
+ },
72807
+ "actionSource.runAction": {
72808
+ capName: "action-source",
72809
+ capScope: "system",
72810
+ addonId: null,
72811
+ access: "create",
72812
+ role: "admin"
72813
+ },
72814
+ "actionSource.stopAction": {
72815
+ capName: "action-source",
72816
+ capScope: "system",
72817
+ addonId: null,
72818
+ access: "create",
72819
+ role: "admin"
72820
+ },
71733
72821
  "addonPages.listPages": {
71734
72822
  capName: "addon-pages",
71735
72823
  capScope: "system",
@@ -73011,6 +74099,13 @@ Object.freeze({
73011
74099
  access: "view",
73012
74100
  role: "admin"
73013
74101
  },
74102
+ "coreBlocks.getAutomationRunState": {
74103
+ capName: "core-blocks",
74104
+ capScope: "system",
74105
+ addonId: null,
74106
+ access: "view",
74107
+ role: "admin"
74108
+ },
73014
74109
  "coreBlocks.getBlockSettings": {
73015
74110
  capName: "core-blocks",
73016
74111
  capScope: "system",
@@ -73032,6 +74127,13 @@ Object.freeze({
73032
74127
  access: "view",
73033
74128
  role: "admin"
73034
74129
  },
74130
+ "coreBlocks.getTraces": {
74131
+ capName: "core-blocks",
74132
+ capScope: "system",
74133
+ addonId: null,
74134
+ access: "view",
74135
+ role: "admin"
74136
+ },
73035
74137
  "coreBlocks.getTypeDefs": {
73036
74138
  capName: "core-blocks",
73037
74139
  capScope: "system",
@@ -73067,6 +74169,13 @@ Object.freeze({
73067
74169
  access: "create",
73068
74170
  role: "admin"
73069
74171
  },
74172
+ "coreBlocks.reportAutomationRun": {
74173
+ capName: "core-blocks",
74174
+ capScope: "system",
74175
+ addonId: null,
74176
+ access: "create",
74177
+ role: "admin"
74178
+ },
73070
74179
  "coreBlocks.restart": {
73071
74180
  capName: "core-blocks",
73072
74181
  capScope: "system",
@@ -74936,6 +76045,13 @@ Object.freeze({
74936
76045
  access: "view",
74937
76046
  role: "viewer"
74938
76047
  },
76048
+ "groupActive.getStatus": {
76049
+ capName: "group-active",
76050
+ capScope: "device",
76051
+ addonId: null,
76052
+ access: "view",
76053
+ role: "viewer"
76054
+ },
74939
76055
  "groupCommands.getStatus": {
74940
76056
  capName: "group-commands",
74941
76057
  capScope: "device",
@@ -75902,6 +77018,13 @@ Object.freeze({
75902
77018
  access: "create",
75903
77019
  role: "admin"
75904
77020
  },
77021
+ "meter.getStatus": {
77022
+ capName: "meter",
77023
+ capScope: "device",
77024
+ addonId: null,
77025
+ access: "view",
77026
+ role: "viewer"
77027
+ },
75905
77028
  "metricsProvider.collectSnapshot": {
75906
77029
  capName: "metrics-provider",
75907
77030
  capScope: "system",
@@ -81626,7 +82749,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81626
82749
  label: t("Full brightness", "Luminosità massima"),
81627
82750
  phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
81628
82751
  icon: "sun",
81629
- args: { percentage: 100 }
82752
+ args: { level: 100 }
81630
82753
  },
81631
82754
  {
81632
82755
  id: "brightness.half",
@@ -81634,7 +82757,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81634
82757
  label: t("Half brightness", "Luminosità a metà"),
81635
82758
  phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
81636
82759
  icon: "sun-dim",
81637
- args: { percentage: 50 }
82760
+ args: { level: 50 }
81638
82761
  },
81639
82762
  {
81640
82763
  id: "brightness.off",
@@ -81642,16 +82765,16 @@ var ACTUATOR_CAP_VOCABULARY = {
81642
82765
  label: t("Brightness off", "Luminosità a zero"),
81643
82766
  phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
81644
82767
  icon: "lightbulb-off",
81645
- args: { percentage: 0 }
82768
+ args: { level: 0 }
81646
82769
  },
81647
82770
  {
81648
82771
  id: "brightness.set",
81649
82772
  method: "setBrightness",
81650
82773
  label: t("Set brightness", "Imposta la luminosità"),
81651
- phrase: t("Set the brightness of {device} to {percentage}", "Imposta la luminosità di {device} a {percentage}"),
82774
+ phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
81652
82775
  icon: "sun-medium",
81653
82776
  openArgs: [{
81654
- key: "percentage",
82777
+ key: "level",
81655
82778
  label: PERCENT,
81656
82779
  offer: {
81657
82780
  kind: "number",
@@ -81665,13 +82788,13 @@ var ACTUATOR_CAP_VOCABULARY = {
81665
82788
  ],
81666
82789
  state: [{
81667
82790
  kind: "numeric",
81668
- field: "percentage",
82791
+ field: "level",
81669
82792
  quantity: t("the brightness", "la luminosità"),
81670
82793
  label: t("Brightness", "Luminosità"),
81671
82794
  unit: "%",
81672
82795
  icon: "sun"
81673
82796
  }],
81674
- events: { onBrightnessChanged: { notOperatorFacing: "the `percentage` state trigger reads the same change" } }
82797
+ events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
81675
82798
  },
81676
82799
  color: {
81677
82800
  scope: "device",
@@ -81685,7 +82808,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81685
82808
  icon: "sun",
81686
82809
  args: { color: {
81687
82810
  mode: "mired",
81688
- mireds: 370
82811
+ colorTemperature: 370
81689
82812
  } }
81690
82813
  },
81691
82814
  {
@@ -81696,7 +82819,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81696
82819
  icon: "snowflake",
81697
82820
  args: { color: {
81698
82821
  mode: "mired",
81699
- mireds: 200
82822
+ colorTemperature: 200
81700
82823
  } }
81701
82824
  },
81702
82825
  {
@@ -82310,7 +83433,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82310
83433
  label: t("Fan off", "Ventilatore spento"),
82311
83434
  phrase: t("Stop {device}", "Ferma {device}"),
82312
83435
  icon: "power-off",
82313
- args: { percentage: 0 }
83436
+ args: { speed: 0 }
82314
83437
  },
82315
83438
  {
82316
83439
  id: "fan.full",
@@ -82318,16 +83441,16 @@ var ACTUATOR_CAP_VOCABULARY = {
82318
83441
  label: t("Fan full", "Ventilatore al massimo"),
82319
83442
  phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
82320
83443
  icon: "fan",
82321
- args: { percentage: 100 }
83444
+ args: { speed: 100 }
82322
83445
  },
82323
83446
  {
82324
83447
  id: "fan.speed",
82325
83448
  method: "setPercentage",
82326
83449
  label: t("Set speed", "Imposta la velocità"),
82327
- phrase: t("Set the speed of {device} to {percentage}", "Imposta la velocità di {device} al {percentage}"),
83450
+ phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
82328
83451
  icon: "fan",
82329
83452
  openArgs: [{
82330
- key: "percentage",
83453
+ key: "speed",
82331
83454
  label: t("Speed", "Velocità"),
82332
83455
  offer: {
82333
83456
  kind: "number",
@@ -82389,7 +83512,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82389
83512
  ],
82390
83513
  state: [{
82391
83514
  kind: "numeric",
82392
- field: "percentage",
83515
+ field: "speed",
82393
83516
  quantity: t("the speed", "la velocità"),
82394
83517
  label: t("Speed", "Velocità"),
82395
83518
  unit: "%",
@@ -83187,7 +84310,7 @@ var APPLIANCE_CAP_VOCABULARY = {
83187
84310
  },
83188
84311
  {
83189
84312
  kind: "numeric",
83190
- field: "progressPercent",
84313
+ field: "progress",
83191
84314
  quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
83192
84315
  label: t("Progress", "Avanzamento"),
83193
84316
  unit: "%",
@@ -83545,10 +84668,10 @@ var APPLIANCE_CAP_VOCABULARY = {
83545
84668
  id: "media.seek",
83546
84669
  method: "seek",
83547
84670
  label: t("Seek", "Vai al punto"),
83548
- phrase: t("Move {device} to {positionMs}", "Porta {device} al punto {positionMs}"),
84671
+ phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
83549
84672
  icon: "fast-forward",
83550
84673
  openArgs: [{
83551
- key: "positionMs",
84674
+ key: "position",
83552
84675
  label: t("Position", "Posizione"),
83553
84676
  offer: {
83554
84677
  kind: "number",
@@ -84000,7 +85123,39 @@ var APPLIANCE_CAP_VOCABULARY = {
84000
85123
  phrase: t("When {device} finishes", "Quando {device} finisce"),
84001
85124
  icon: "square"
84002
85125
  }
84003
- }
85126
+ },
85127
+ overrides: { byType: {
85128
+ [DeviceType.Washer]: {
85129
+ state: { running: {
85130
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
85131
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
85132
+ } },
85133
+ events: {
85134
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
85135
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
85136
+ }
85137
+ },
85138
+ [DeviceType.Dryer]: {
85139
+ state: { running: {
85140
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
85141
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
85142
+ } },
85143
+ events: {
85144
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
85145
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
85146
+ }
85147
+ },
85148
+ [DeviceType.Dishwasher]: {
85149
+ state: { running: {
85150
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
85151
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
85152
+ } },
85153
+ events: {
85154
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
85155
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
85156
+ }
85157
+ }
85158
+ } }
84004
85159
  },
84005
85160
  update: {
84006
85161
  scope: "device",
@@ -84565,7 +85720,7 @@ var SENSOR_CAP_VOCABULARY = {
84565
85720
  state: [
84566
85721
  {
84567
85722
  kind: "numeric",
84568
- field: "co2Ppm",
85723
+ field: "co2",
84569
85724
  quantity: t("the CO₂", "la CO₂"),
84570
85725
  label: t("CO₂", "CO₂"),
84571
85726
  unit: "ppm",
@@ -84573,7 +85728,7 @@ var SENSOR_CAP_VOCABULARY = {
84573
85728
  },
84574
85729
  {
84575
85730
  kind: "numeric",
84576
- field: "vocPpb",
85731
+ field: "voc",
84577
85732
  quantity: t("the VOC level", "il livello di COV"),
84578
85733
  label: t("VOC", "COV"),
84579
85734
  unit: "ppb",
@@ -84609,7 +85764,7 @@ var SENSOR_CAP_VOCABULARY = {
84609
85764
  noun: t("Light level", "Luminosità ambiente"),
84610
85765
  state: [{
84611
85766
  kind: "numeric",
84612
- field: "lux",
85767
+ field: "illuminance",
84613
85768
  quantity: t("the light level", "la luminosità"),
84614
85769
  label: t("Illuminance", "Illuminamento"),
84615
85770
  unit: "lx",
@@ -84772,6 +85927,10 @@ var SENSOR_CAP_VOCABULARY = {
84772
85927
  icon: "flame"
84773
85928
  }]
84774
85929
  },
85930
+ "group-active": {
85931
+ scope: "device",
85932
+ noun: t("Group state", "Stato del gruppo")
85933
+ },
84775
85934
  "group-commands": {
84776
85935
  scope: "device",
84777
85936
  noun: t("Group command", "Comando di gruppo")
@@ -84808,7 +85967,7 @@ var SENSOR_CAP_VOCABULARY = {
84808
85967
  noun: t("Humidity", "Umidità"),
84809
85968
  state: [{
84810
85969
  kind: "numeric",
84811
- field: "percent",
85970
+ field: "humidity",
84812
85971
  quantity: t("the humidity", "l’umidità"),
84813
85972
  label: t("Humidity", "Umidità"),
84814
85973
  unit: "%",
@@ -84828,6 +85987,17 @@ var SENSOR_CAP_VOCABULARY = {
84828
85987
  }],
84829
85988
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
84830
85989
  },
85990
+ meter: {
85991
+ scope: "device",
85992
+ noun: t("Meter", "Contatore"),
85993
+ state: [{
85994
+ kind: "numeric",
85995
+ field: "value",
85996
+ quantity: t("the total", "il totale"),
85997
+ label: t("Total", "Totale"),
85998
+ icon: "gauge"
85999
+ }]
86000
+ },
84831
86001
  "numeric-sensor": {
84832
86002
  scope: "device",
84833
86003
  noun: t("Sensor", "Sensore"),
@@ -84845,7 +86015,7 @@ var SENSOR_CAP_VOCABULARY = {
84845
86015
  state: [
84846
86016
  {
84847
86017
  kind: "numeric",
84848
- field: "watts",
86018
+ field: "power",
84849
86019
  quantity: t("the power draw", "il consumo"),
84850
86020
  label: t("Power", "Potenza"),
84851
86021
  unit: "W",
@@ -84853,7 +86023,7 @@ var SENSOR_CAP_VOCABULARY = {
84853
86023
  },
84854
86024
  {
84855
86025
  kind: "numeric",
84856
- field: "kwhTotal",
86026
+ field: "energy",
84857
86027
  quantity: t("the energy used", "l’energia consumata"),
84858
86028
  label: t("Energy", "Energia"),
84859
86029
  unit: "kWh",
@@ -84861,7 +86031,7 @@ var SENSOR_CAP_VOCABULARY = {
84861
86031
  },
84862
86032
  {
84863
86033
  kind: "numeric",
84864
- field: "volts",
86034
+ field: "voltage",
84865
86035
  quantity: t("the voltage", "la tensione"),
84866
86036
  label: t("Voltage", "Tensione"),
84867
86037
  unit: "V",
@@ -84869,7 +86039,7 @@ var SENSOR_CAP_VOCABULARY = {
84869
86039
  },
84870
86040
  {
84871
86041
  kind: "numeric",
84872
- field: "amps",
86042
+ field: "current",
84873
86043
  quantity: t("the current", "la corrente"),
84874
86044
  label: t("Current", "Corrente"),
84875
86045
  unit: "A",
@@ -84886,13 +86056,6 @@ var SENSOR_CAP_VOCABULARY = {
84886
86056
  label: t("Where", "Posizione"),
84887
86057
  phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
84888
86058
  icon: "map-pin"
84889
- }, {
84890
- kind: "numeric",
84891
- field: "batteryPercent",
84892
- quantity: t("the phone battery", "la batteria del telefono"),
84893
- label: t("Battery", "Batteria"),
84894
- unit: "%",
84895
- icon: "battery"
84896
86059
  }]
84897
86060
  },
84898
86061
  "pressure-sensor": {
@@ -84900,7 +86063,7 @@ var SENSOR_CAP_VOCABULARY = {
84900
86063
  noun: t("Pressure", "Pressione"),
84901
86064
  state: [{
84902
86065
  kind: "numeric",
84903
- field: "hpa",
86066
+ field: "pressure",
84904
86067
  quantity: t("the pressure", "la pressione"),
84905
86068
  label: t("Pressure", "Pressione"),
84906
86069
  unit: "hPa",
@@ -84936,7 +86099,7 @@ var SENSOR_CAP_VOCABULARY = {
84936
86099
  noun: t("Temperature", "Temperatura"),
84937
86100
  state: [{
84938
86101
  kind: "numeric",
84939
- field: "celsius",
86102
+ field: "temperature",
84940
86103
  quantity: t("the temperature", "la temperatura"),
84941
86104
  label: t("Temperature", "Temperatura"),
84942
86105
  unit: "°C",
@@ -85007,7 +86170,7 @@ var SENSOR_CAP_VOCABULARY = {
85007
86170
  state: [
85008
86171
  {
85009
86172
  kind: "numeric",
85010
- field: "percentage",
86173
+ field: "level",
85011
86174
  quantity: t("the battery level", "il livello della batteria"),
85012
86175
  label: t("Battery", "Batteria"),
85013
86176
  unit: "%",
@@ -85049,7 +86212,7 @@ var SENSOR_CAP_VOCABULARY = {
85049
86212
  icon: "battery-charging"
85050
86213
  }
85051
86214
  ],
85052
- events: { onStatusChanged: { notOperatorFacing: "the percentage / sleeping / charging state triggers each read one field of this change" } },
86215
+ events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
85053
86216
  extraEvents: [{
85054
86217
  category: EventCategory.DeviceAwake,
85055
86218
  label: t("Woke up", "Risvegliato"),
@@ -85067,7 +86230,7 @@ var SENSOR_CAP_VOCABULARY = {
85067
86230
  noun: t("Network link", "Collegamento di rete"),
85068
86231
  state: [{
85069
86232
  kind: "numeric",
85070
- field: "signalPercent",
86233
+ field: "signal",
85071
86234
  quantity: t("the signal strength", "l’intensità del segnale"),
85072
86235
  label: t("Signal", "Segnale"),
85073
86236
  unit: "%",
@@ -85160,6 +86323,8 @@ var ML = "model and training machinery — no device action";
85160
86323
  var INTERNAL = "internal plumbing between addons — never operator-facing";
85161
86324
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
85162
86325
  var SYSTEM_CAP_VOCABULARY = {
86326
+ "action-source": system(INTERNAL),
86327
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
85163
86328
  "addon-pages": system(UI_PLUMBING),
85164
86329
  "addon-pages-source": system(UI_PLUMBING),
85165
86330
  "addon-routes": system(UI_PLUMBING),
@@ -85300,8 +86465,47 @@ function projectAction(cap, action) {
85300
86465
  };
85301
86466
  }
85302
86467
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
85303
- 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;
86468
+ 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;
85304
86469
  DeviceFeature.BatteryOperated;
86470
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
86471
+ 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.";
86472
+ 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.";
86473
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
86474
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
86475
+ 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.";
86476
+ function withheld(reason) {
86477
+ return {
86478
+ [DeviceType.Washer]: {
86479
+ exported: false,
86480
+ reason
86481
+ },
86482
+ [DeviceType.Dryer]: {
86483
+ exported: false,
86484
+ reason
86485
+ },
86486
+ [DeviceType.Dishwasher]: {
86487
+ exported: false,
86488
+ reason
86489
+ }
86490
+ };
86491
+ }
86492
+ function offered(received) {
86493
+ return {
86494
+ [DeviceType.Washer]: {
86495
+ exported: true,
86496
+ as: received
86497
+ },
86498
+ [DeviceType.Dryer]: {
86499
+ exported: true,
86500
+ as: received
86501
+ },
86502
+ [DeviceType.Dishwasher]: {
86503
+ exported: true,
86504
+ as: received
86505
+ }
86506
+ };
86507
+ }
86508
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
85305
86509
  Object.freeze({
85306
86510
  "accessories.getStatus": [{
85307
86511
  name: "deviceId",
@@ -85317,6 +86521,31 @@ Object.freeze({
85317
86521
  form: "single",
85318
86522
  optional: false
85319
86523
  }],
86524
+ "actions.get": [{
86525
+ name: "deviceId",
86526
+ form: "single",
86527
+ optional: true
86528
+ }],
86529
+ "actions.getLinks": [{
86530
+ name: "deviceId",
86531
+ form: "single",
86532
+ optional: false
86533
+ }],
86534
+ "actions.listForCamera": [{
86535
+ name: "deviceId",
86536
+ form: "single",
86537
+ optional: false
86538
+ }],
86539
+ "actions.run": [{
86540
+ name: "deviceId",
86541
+ form: "single",
86542
+ optional: true
86543
+ }],
86544
+ "actions.setLinks": [{
86545
+ name: "deviceId",
86546
+ form: "single",
86547
+ optional: false
86548
+ }],
85320
86549
  "addonSettings.getDeviceSettings": [{
85321
86550
  name: "deviceId",
85322
86551
  form: "single",
@@ -86268,6 +87497,11 @@ Object.freeze({
86268
87497
  form: "single",
86269
87498
  optional: false
86270
87499
  }],
87500
+ "groupActive.getStatus": [{
87501
+ name: "deviceId",
87502
+ form: "single",
87503
+ optional: false
87504
+ }],
86271
87505
  "groupCommands.getStatus": [{
86272
87506
  name: "deviceId",
86273
87507
  form: "single",
@@ -86478,6 +87712,11 @@ Object.freeze({
86478
87712
  form: "single",
86479
87713
  optional: false
86480
87714
  }],
87715
+ "meter.getStatus": [{
87716
+ name: "deviceId",
87717
+ form: "single",
87718
+ optional: false
87719
+ }],
86481
87720
  "motion.getStatus": [{
86482
87721
  name: "deviceId",
86483
87722
  form: "single",
@@ -88085,7 +89324,6 @@ Object.freeze({
88085
89324
  "network-access": "ingress",
88086
89325
  "smtp-provider": "email"
88087
89326
  });
88088
- DeviceType.Camera;
88089
89327
  /**
88090
89328
  * A saved Customize assignment (D936). The schema is the type: callers use
88091
89329
  * `z.infer`, they do not restate the shape.
@@ -89389,6 +90627,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
89389
90627
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
89390
90628
  DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
89391
90629
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
90630
+ String.raw`(?<=^|["'(\s>,\[{])`;
89392
90631
  var MB = 1024 * 1024;
89393
90632
  1024 * MB, 3072 * MB;
89394
90633
  //#endregion
@@ -90100,7 +91339,7 @@ var TuyaTemperatureSensorDevice = class extends TuyaChildDevice {
90100
91339
  }
90101
91340
  #slice(celsius) {
90102
91341
  return {
90103
- celsius,
91342
+ temperature: celsius,
90104
91343
  lastFetchedAt: Date.now()
90105
91344
  };
90106
91345
  }
@@ -90112,7 +91351,7 @@ var TuyaTemperatureSensorDevice = class extends TuyaChildDevice {
90112
91351
  recomputeSlice() {
90113
91352
  const celsius = this.#read();
90114
91353
  if (celsius === null) return;
90115
- if (this.runtimeState.getCapState(CAP_NAME$1)?.celsius === celsius) return;
91354
+ if (this.runtimeState.getCapState(CAP_NAME$1)?.temperature === celsius) return;
90116
91355
  this.runtimeState.setCapState(CAP_NAME$1, this.#slice(celsius));
90117
91356
  }
90118
91357
  };