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