@camstack/addon-provider-unifi 0.2.148 → 0.2.150

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
2
+ //#region ../types/dist/event-category-DmZDPDf_.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -626,7 +626,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
626
626
  * (a track was promoted to a stationary-registry entry) or departs (the
627
627
  * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
628
628
  * the dedicated "Stationary" section — never the live event feed. Payload:
629
- * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
629
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
630
630
  */
631
631
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
632
632
  /**
@@ -764,6 +764,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
764
764
  */
765
765
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
766
766
  /**
767
+ * An action run started or ended. Emitted by the `actions` surface.
768
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
769
+ */
770
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
771
+ /**
767
772
  * Progress update from a running model conversion job.
768
773
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
769
774
  * Emitted by `addon-model-studio` on the converting node.
@@ -5442,7 +5447,7 @@ var ZodIssueCode = {
5442
5447
  var ZodFirstPartyTypeKind;
5443
5448
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5444
5449
  //#endregion
5445
- //#region ../types/dist/sleep-bykKrTgu.mjs
5450
+ //#region ../types/dist/sleep-Crlmu_tO.mjs
5446
5451
  /**
5447
5452
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5448
5453
  * window to float samples (D455).
@@ -8562,6 +8567,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
8562
8567
  /** Every capability name, so addon code can check a name without the schema graph. */
8563
8568
  var CAP_NAMES = Object.freeze([
8564
8569
  "accessories",
8570
+ "action-source",
8571
+ "actions",
8565
8572
  "addon-pages",
8566
8573
  "addon-pages-source",
8567
8574
  "addon-routes",
@@ -8635,6 +8642,7 @@ var CAP_NAMES = Object.freeze([
8635
8642
  "filesystem-browse",
8636
8643
  "flood",
8637
8644
  "gas",
8645
+ "group-active",
8638
8646
  "group-commands",
8639
8647
  "group-status",
8640
8648
  "heating-valve",
@@ -8657,6 +8665,7 @@ var CAP_NAMES = Object.freeze([
8657
8665
  "login-method",
8658
8666
  "media-player",
8659
8667
  "mesh-network",
8668
+ "meter",
8660
8669
  "metrics-provider",
8661
8670
  "model-convert",
8662
8671
  "model-distributor",
@@ -8748,11 +8757,11 @@ var CAP_NAMES = Object.freeze([
8748
8757
  Object.freeze({
8749
8758
  "air-quality-sensor": [
8750
8759
  {
8751
- "path": "co2Ppm",
8760
+ "path": "co2",
8752
8761
  "valueType": "number"
8753
8762
  },
8754
8763
  {
8755
- "path": "vocPpb",
8764
+ "path": "voc",
8756
8765
  "valueType": "number"
8757
8766
  },
8758
8767
  {
@@ -8802,7 +8811,7 @@ Object.freeze({
8802
8811
  }
8803
8812
  ],
8804
8813
  "ambient-light-sensor": [{
8805
- "path": "lux",
8814
+ "path": "illuminance",
8806
8815
  "valueType": "number"
8807
8816
  }],
8808
8817
  "audio-metrics": [
@@ -8863,7 +8872,7 @@ Object.freeze({
8863
8872
  ],
8864
8873
  "battery": [
8865
8874
  {
8866
- "path": "percentage",
8875
+ "path": "level",
8867
8876
  "valueType": "number"
8868
8877
  },
8869
8878
  {
@@ -8885,7 +8894,7 @@ Object.freeze({
8885
8894
  "valueType": "number"
8886
8895
  },
8887
8896
  {
8888
- "path": "volts",
8897
+ "path": "voltage",
8889
8898
  "valueType": "number"
8890
8899
  },
8891
8900
  {
@@ -8906,7 +8915,7 @@ Object.freeze({
8906
8915
  "valueType": "boolean"
8907
8916
  }],
8908
8917
  "brightness": [{
8909
- "path": "percentage",
8918
+ "path": "level",
8910
8919
  "valueType": "number"
8911
8920
  }],
8912
8921
  "broker": [{
@@ -9107,7 +9116,7 @@ Object.freeze({
9107
9116
  "valueType": "number"
9108
9117
  },
9109
9118
  {
9110
- "path": "mireds",
9119
+ "path": "colorTemperature",
9111
9120
  "valueType": "number"
9112
9121
  }
9113
9122
  ],
@@ -9286,15 +9295,15 @@ Object.freeze({
9286
9295
  "valueType": "string"
9287
9296
  },
9288
9297
  {
9289
- "path": "remainingSeconds",
9298
+ "path": "remaining",
9290
9299
  "valueType": "number"
9291
9300
  },
9292
9301
  {
9293
- "path": "delayedStartSeconds",
9302
+ "path": "delayedStart",
9294
9303
  "valueType": "number"
9295
9304
  },
9296
9305
  {
9297
- "path": "progressPercent",
9306
+ "path": "progress",
9298
9307
  "valueType": "number"
9299
9308
  },
9300
9309
  {
@@ -9408,7 +9417,7 @@ Object.freeze({
9408
9417
  ],
9409
9418
  "fan-control": [
9410
9419
  {
9411
- "path": "percentage",
9420
+ "path": "speed",
9412
9421
  "valueType": "number"
9413
9422
  },
9414
9423
  {
@@ -9455,6 +9464,10 @@ Object.freeze({
9455
9464
  "path": "detected",
9456
9465
  "valueType": "boolean"
9457
9466
  }],
9467
+ "group-active": [{
9468
+ "path": "active",
9469
+ "valueType": "boolean"
9470
+ }],
9458
9471
  "group-status": [
9459
9472
  {
9460
9473
  "path": "included",
@@ -9504,7 +9517,7 @@ Object.freeze({
9504
9517
  }
9505
9518
  ],
9506
9519
  "humidity-sensor": [{
9507
- "path": "percent",
9520
+ "path": "humidity",
9508
9521
  "valueType": "number"
9509
9522
  }],
9510
9523
  "image": [{
@@ -9614,7 +9627,7 @@ Object.freeze({
9614
9627
  "valueType": "number"
9615
9628
  },
9616
9629
  {
9617
- "path": "progressPercent",
9630
+ "path": "progress",
9618
9631
  "valueType": "number"
9619
9632
  },
9620
9633
  {
@@ -9693,11 +9706,11 @@ Object.freeze({
9693
9706
  "valueType": "string"
9694
9707
  },
9695
9708
  {
9696
- "path": "positionMs",
9709
+ "path": "position",
9697
9710
  "valueType": "number"
9698
9711
  },
9699
9712
  {
9700
- "path": "durationMs",
9713
+ "path": "duration",
9701
9714
  "valueType": "number"
9702
9715
  },
9703
9716
  {
@@ -9714,6 +9727,10 @@ Object.freeze({
9714
9727
  ]
9715
9728
  }
9716
9729
  ],
9730
+ "meter": [{
9731
+ "path": "value",
9732
+ "valueType": "number"
9733
+ }],
9717
9734
  "motion": [
9718
9735
  {
9719
9736
  "path": "detected",
@@ -9795,11 +9812,11 @@ Object.freeze({
9795
9812
  ]
9796
9813
  },
9797
9814
  {
9798
- "path": "signalPercent",
9815
+ "path": "signal",
9799
9816
  "valueType": "number"
9800
9817
  },
9801
9818
  {
9802
- "path": "rssiDbm",
9819
+ "path": "rssi",
9803
9820
  "valueType": "number"
9804
9821
  },
9805
9822
  {
@@ -9854,6 +9871,18 @@ Object.freeze({
9854
9871
  "path": "foodLevel",
9855
9872
  "valueType": "number"
9856
9873
  },
9874
+ {
9875
+ "path": "bowlFood",
9876
+ "valueType": "number"
9877
+ },
9878
+ {
9879
+ "path": "eating",
9880
+ "valueType": "boolean"
9881
+ },
9882
+ {
9883
+ "path": "supportsMarkRefilled",
9884
+ "valueType": "boolean"
9885
+ },
9857
9886
  {
9858
9887
  "path": "food1",
9859
9888
  "valueType": "number"
@@ -9918,23 +9947,23 @@ Object.freeze({
9918
9947
  ],
9919
9948
  "power-meter": [
9920
9949
  {
9921
- "path": "watts",
9950
+ "path": "power",
9922
9951
  "valueType": "number"
9923
9952
  },
9924
9953
  {
9925
- "path": "kwhTotal",
9954
+ "path": "energy",
9926
9955
  "valueType": "number"
9927
9956
  },
9928
9957
  {
9929
- "path": "volts",
9958
+ "path": "voltage",
9930
9959
  "valueType": "number"
9931
9960
  },
9932
9961
  {
9933
- "path": "amps",
9962
+ "path": "current",
9934
9963
  "valueType": "number"
9935
9964
  },
9936
9965
  {
9937
- "path": "va",
9966
+ "path": "apparentPower",
9938
9967
  "valueType": "number"
9939
9968
  },
9940
9969
  {
@@ -9962,14 +9991,10 @@ Object.freeze({
9962
9991
  {
9963
9992
  "path": "gps.accuracyMeters",
9964
9993
  "valueType": "number"
9965
- },
9966
- {
9967
- "path": "batteryPercent",
9968
- "valueType": "number"
9969
9994
  }
9970
9995
  ],
9971
9996
  "pressure-sensor": [{
9972
- "path": "hpa",
9997
+ "path": "pressure",
9973
9998
  "valueType": "number"
9974
9999
  }],
9975
10000
  "privacy-mask": [{
@@ -10283,7 +10308,7 @@ Object.freeze({
10283
10308
  "valueType": "boolean"
10284
10309
  }],
10285
10310
  "temperature-sensor": [{
10286
- "path": "celsius",
10311
+ "path": "temperature",
10287
10312
  "valueType": "number"
10288
10313
  }],
10289
10314
  "update": [
@@ -10331,7 +10356,7 @@ Object.freeze({
10331
10356
  "valueType": "string"
10332
10357
  },
10333
10358
  {
10334
- "path": "progressPercent",
10359
+ "path": "progress",
10335
10360
  "valueType": "number"
10336
10361
  },
10337
10362
  {
@@ -10471,7 +10496,7 @@ function userError(code, params, englishFallback) {
10471
10496
  return new CamstackError(code, params, englishFallback);
10472
10497
  }
10473
10498
  //#endregion
10474
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10499
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10475
10500
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10476
10501
  DeviceType["Camera"] = "camera";
10477
10502
  DeviceType["Hub"] = "hub";
@@ -10565,8 +10590,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10565
10590
  DeviceType["PetFeeder"] = "pet-feeder";
10566
10591
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10567
10592
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10568
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10593
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10594
+ * A plug-built dishwasher carries running-state and power instead. */
10569
10595
  DeviceType["Dishwasher"] = "dishwasher";
10596
+ /** Washing machine. A plug-built one is running while its power stays
10597
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10598
+ DeviceType["Washer"] = "washer";
10599
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10600
+ DeviceType["Dryer"] = "dryer";
10570
10601
  return DeviceType;
10571
10602
  }({});
10572
10603
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -10824,7 +10855,7 @@ function errMsg(err) {
10824
10855
  return String(err);
10825
10856
  }
10826
10857
  //#endregion
10827
- //#region ../types/dist/composition-OhpuespA.mjs
10858
+ //#region ../types/dist/composition-CZImLpOI.mjs
10828
10859
  /**
10829
10860
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10830
10861
  *
@@ -11139,6 +11170,13 @@ var table = {
11139
11170
  apply: () => {
11140
11171
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11141
11172
  }
11173
+ },
11174
+ schmitt: {
11175
+ minArgs: 3,
11176
+ maxArgs: 5,
11177
+ apply: () => {
11178
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11179
+ }
11142
11180
  }
11143
11181
  };
11144
11182
  Object.freeze(Object.assign(Object.create(null), table));
@@ -11580,6 +11618,99 @@ function validateExpressionSource(src) {
11580
11618
  return null;
11581
11619
  }
11582
11620
  /**
11621
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11622
+ *
11623
+ * Standby of the control board is under 5 W; a motor or a heater is far
11624
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11625
+ * a cycle, so running starts only after 30 s above the line.
11626
+ *
11627
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11628
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11629
+ * report "finished" in the middle of the program, so the stop dwell is
11630
+ * 10 minutes on every type. The number is stored per type so one machine
11631
+ * can move later without moving the others.
11632
+ */
11633
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11634
+ var APPLIANCE_KINDS = [
11635
+ "washer",
11636
+ "dryer",
11637
+ "dishwasher"
11638
+ ];
11639
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11640
+ /**
11641
+ * Statistics and integrals over a history read (D944).
11642
+ *
11643
+ * Pure: the same points always give the same answer, so a restart recomputes
11644
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11645
+ * never the mean of the samples. A hole with no held value is not integrated
11646
+ * as zero. A truncated read is not published.
11647
+ */
11648
+ var STATISTIC_STATS = [
11649
+ "min",
11650
+ "max",
11651
+ "mean",
11652
+ "sum",
11653
+ "count",
11654
+ "change"
11655
+ ];
11656
+ /**
11657
+ * The range a statistics or integral helper asks history for (D944).
11658
+ *
11659
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11660
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11661
+ * is strictly longer than the longest that range can be. Forever qualifies.
11662
+ * Equal to a tier's retention is a refusal, not a short answer.
11663
+ */
11664
+ var MINUTE_MS = 6e4;
11665
+ var HOUR_MS = 60 * MINUTE_MS;
11666
+ var DAY_MS = 24 * HOUR_MS;
11667
+ var ROLLING_SPANS = [
11668
+ "1h",
11669
+ "24h",
11670
+ "2d",
11671
+ "7d",
11672
+ "30d",
11673
+ "custom"
11674
+ ];
11675
+ var CALENDAR_PERIODS = [
11676
+ "today",
11677
+ "yesterday",
11678
+ "this-week",
11679
+ "this-month"
11680
+ ];
11681
+ var RESET_PERIODS = [
11682
+ "never",
11683
+ "daily",
11684
+ "weekly",
11685
+ "monthly"
11686
+ ];
11687
+ 5 * MINUTE_MS;
11688
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
11689
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
11690
+ var rollingWindowObject = object({
11691
+ kind: literal("rolling"),
11692
+ span: _enum(ROLLING_SPANS),
11693
+ customMinutes: number().int().min(5).optional()
11694
+ }).check((payload) => {
11695
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
11696
+ code: "custom",
11697
+ message: "a custom range needs a duration in minutes",
11698
+ path: ["customMinutes"],
11699
+ input: payload.value
11700
+ });
11701
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
11702
+ code: "custom",
11703
+ message: "only a custom range carries a duration",
11704
+ path: ["customMinutes"],
11705
+ input: payload.value
11706
+ });
11707
+ });
11708
+ var RollingWindowSchema = rollingWindowObject;
11709
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
11710
+ kind: literal("calendar"),
11711
+ period: _enum(CALENDAR_PERIODS)
11712
+ })]);
11713
+ /**
11583
11714
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11584
11715
  * id. Split out so helper schemas can name it without a cycle through
11585
11716
  * `composition.ts` (D659).
@@ -11605,7 +11736,13 @@ var GROUP_TEMPLATE_IDS = [
11605
11736
  "occupancy",
11606
11737
  "binary",
11607
11738
  "switch",
11608
- "light"
11739
+ "light",
11740
+ "fan",
11741
+ "cover",
11742
+ "valve",
11743
+ "lock",
11744
+ "media-player",
11745
+ "climate"
11609
11746
  ];
11610
11747
  var NUMERIC_AGGREGATES = [
11611
11748
  "mean",
@@ -11635,14 +11772,73 @@ var HelperMemberSchema = object({
11635
11772
  cap: string().min(1),
11636
11773
  fieldPath: string().min(1)
11637
11774
  });
11638
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11639
- kind: literal("group"),
11640
- templateId: GroupTemplateIdSchema,
11641
- aggregate: AggregateReducerSchema,
11642
- members: array(HelperMemberSchema).min(1).max(64),
11643
- /** Actuator groups only. Readings leave this unset. */
11644
- active: ActiveCriterionSchema.optional()
11645
- })]);
11775
+ var HelperSpecSchema = discriminatedUnion("kind", [
11776
+ object({
11777
+ kind: literal("group"),
11778
+ templateId: GroupTemplateIdSchema,
11779
+ aggregate: AggregateReducerSchema,
11780
+ members: array(HelperMemberSchema).min(1).max(64),
11781
+ /** Actuator groups only. Readings leave this unset. */
11782
+ active: ActiveCriterionSchema.optional()
11783
+ }),
11784
+ object({
11785
+ kind: literal("appliance"),
11786
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
11787
+ appliance: _enum(APPLIANCE_KINDS),
11788
+ thresholdWatts: number().positive(),
11789
+ startDwellMs: number().int().nonnegative(),
11790
+ stopDwellMs: number().int().nonnegative(),
11791
+ member: HelperMemberSchema
11792
+ }),
11793
+ object({
11794
+ kind: literal("statistic"),
11795
+ stat: _enum(STATISTIC_STATS),
11796
+ window: StatisticWindowSchema,
11797
+ source: HelperMemberSchema
11798
+ }),
11799
+ object({
11800
+ kind: literal("integral"),
11801
+ reset: _enum(RESET_PERIODS),
11802
+ output: _enum(["energy", "meter"]),
11803
+ source: HelperMemberSchema
11804
+ }),
11805
+ object({
11806
+ kind: literal("threshold"),
11807
+ source: HelperMemberSchema,
11808
+ high: number(),
11809
+ low: number(),
11810
+ onForMs: number().int().nonnegative(),
11811
+ offForMs: number().int().nonnegative()
11812
+ }),
11813
+ object({
11814
+ kind: literal("arithmetic"),
11815
+ op: _enum([
11816
+ "sum",
11817
+ "difference",
11818
+ "ratio"
11819
+ ]),
11820
+ inputs: array(HelperMemberSchema).min(2).max(64)
11821
+ }),
11822
+ object({
11823
+ kind: literal("expression"),
11824
+ expr: string().min(1),
11825
+ inputs: array(object({
11826
+ name: string().min(1),
11827
+ member: HelperMemberSchema
11828
+ })).min(1).max(64),
11829
+ output: _enum(["number", "boolean"])
11830
+ }),
11831
+ object({
11832
+ kind: literal("derivative"),
11833
+ window: RollingWindowSchema,
11834
+ per: _enum([
11835
+ "second",
11836
+ "minute",
11837
+ "hour"
11838
+ ]),
11839
+ source: HelperMemberSchema
11840
+ })
11841
+ ]);
11646
11842
  /** A composed device's stableId is this prefix plus the block id. */
11647
11843
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11648
11844
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -11703,6 +11899,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11703
11899
  "all"
11704
11900
  ]),
11705
11901
  reads: array(CompositionFieldReadSchema).min(1).max(64),
11902
+ /**
11903
+ * Set when the reads are an enum state, not a yes/no: the listed states
11904
+ * count as active (a cover is `open`). A state outside the list is inactive;
11905
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
11906
+ */
11907
+ activeWhen: array(string().min(1)).min(1).optional(),
11706
11908
  /** `value` is the reading; the others are the member counts. */
11707
11909
  publish: _enum([
11708
11910
  "value",
@@ -11716,6 +11918,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11716
11918
  "omit",
11717
11919
  "offline"
11718
11920
  ])
11921
+ }),
11922
+ object({
11923
+ kind: literal("series"),
11924
+ read: CompositionFieldReadSchema,
11925
+ /** `value` is the statistic or the integral. The others are the gap counts. */
11926
+ publish: _enum([
11927
+ "value",
11928
+ "included",
11929
+ "excluded",
11930
+ "total"
11931
+ ]),
11932
+ /**
11933
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
11934
+ * device unseeded until history answers, so a restart cannot publish 0.
11935
+ */
11936
+ empty: _enum([
11937
+ "null",
11938
+ "omit",
11939
+ "offline"
11940
+ ])
11719
11941
  })
11720
11942
  ]);
11721
11943
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -15153,6 +15375,14 @@ var AutomationConditionLeafSchema = object({
15153
15375
  array(union([string(), number()]))
15154
15376
  ])
15155
15377
  });
15378
+ var AutomationConditionStationaryPresentSchema = object({
15379
+ kind: literal("stationary-present"),
15380
+ deviceId: number().int().nonnegative(),
15381
+ /** Class names; a macro class matches every raw class that maps to it. */
15382
+ classes: array(string().min(1)).min(1).optional(),
15383
+ /** The parked object must be in at least one of these zones. */
15384
+ zones: array(string().min(1)).min(1).optional()
15385
+ });
15156
15386
  /**
15157
15387
  * The expression leaf, declared as a plain object rather than an intersection
15158
15388
  * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
@@ -15186,7 +15416,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
15186
15416
  child: AutomationConditionSchema
15187
15417
  }),
15188
15418
  AutomationConditionLeafSchema,
15189
- AutomationConditionExpressionSchema
15419
+ AutomationConditionExpressionSchema,
15420
+ AutomationConditionStationaryPresentSchema
15190
15421
  ]));
15191
15422
  /**
15192
15423
  * What starts a run.
@@ -15227,7 +15458,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15227
15458
  kind: literal("schedule"),
15228
15459
  cron: string().min(1).max(120)
15229
15460
  }),
15230
- object({ kind: literal("manual") })
15461
+ object({ kind: literal("manual") }),
15462
+ object({
15463
+ kind: literal("detection"),
15464
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
15465
+ * matches every raw class that maps to it; any other name matches
15466
+ * exactly. */
15467
+ classes: array(string().min(1)).min(1),
15468
+ deviceIds: array(number().int().nonnegative()).min(1),
15469
+ /** The track must have visited at least one of these zones. */
15470
+ zones: array(string().min(1)).min(1).optional(),
15471
+ /** Best-so-far confidence the track must reach. */
15472
+ minScore: number().min(0).max(1).optional()
15473
+ }),
15474
+ object({
15475
+ kind: literal("stationary"),
15476
+ deviceIds: array(number().int().nonnegative()).min(1),
15477
+ /** Class names; a macro class matches like the detection trigger. */
15478
+ classes: array(string().min(1)).min(1).optional(),
15479
+ /** The parked object must be in at least one of these zones. */
15480
+ zones: array(string().min(1)).min(1).optional(),
15481
+ /** `appeared`: it became parked. `departed`: the registry retired it.
15482
+ * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
15483
+ phase: _enum(["appeared", "departed"])
15484
+ }),
15485
+ object({
15486
+ kind: literal("interval"),
15487
+ minutes: number().int().min(1).max(1440)
15488
+ })
15231
15489
  ]);
15232
15490
  /**
15233
15491
  * One action step.
@@ -15247,28 +15505,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15247
15505
  * is called. The runner still receives a number and is untouched; the
15248
15506
  * resolution is the recipe's job, not the runner's.
15249
15507
  */
15508
+ var AutomationWaitStepSchema = object({
15509
+ kind: literal("wait"),
15510
+ seconds: number().min(0).max(300)
15511
+ });
15512
+ var AutomationCapStepSchema = object({
15513
+ kind: literal("cap"),
15514
+ /**
15515
+ * One device, several same-family devices (locks / climate family / …),
15516
+ * or `{ $var }` for the device that triggered the run. An array is
15517
+ * fan-out at codegen — each id gets the same cap.method call.
15518
+ */
15519
+ deviceId: union([
15520
+ number().int(),
15521
+ array(number().int()).min(1),
15522
+ object({ $var: string().min(1) })
15523
+ ]),
15524
+ cap: string().min(1),
15525
+ method: string().min(1),
15526
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
15527
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
15528
+ * the expression leaf. */
15529
+ args: record(string(), unknown()).optional()
15530
+ });
15531
+ /**
15532
+ * The steps an internal Action may contain: cap and wait, and nothing that can
15533
+ * call back into an Action — so an Action can never reach itself (no cycles).
15534
+ */
15535
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
15250
15536
  var AutomationActionSchema = discriminatedUnion("kind", [
15537
+ AutomationWaitStepSchema,
15538
+ AutomationCapStepSchema,
15251
15539
  object({
15252
- kind: literal("wait"),
15253
- seconds: number().min(0).max(300)
15254
- }),
15255
- object({
15256
- kind: literal("cap"),
15257
- /**
15258
- * One device, several same-family devices (locks / climate family / …),
15259
- * or `{ $var }` for the device that triggered the run. An array is
15260
- * fan-out at codegen — each id gets the same cap.method call.
15261
- */
15262
- deviceId: union([
15263
- number().int(),
15264
- array(number().int()).min(1),
15265
- object({ $var: string().min(1) })
15266
- ]),
15267
- cap: string().min(1),
15268
- method: string().min(1),
15269
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
15270
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
15271
- args: record(string(), unknown()).optional()
15540
+ kind: literal("action"),
15541
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
15542
+ actionId: string().min(1),
15543
+ /** Values for the action's declared variables; the others take their default. */
15544
+ variables: record(string(), union([
15545
+ string(),
15546
+ number(),
15547
+ boolean()
15548
+ ])).optional()
15272
15549
  }),
15273
15550
  object({
15274
15551
  kind: literal("code"),
@@ -15277,10 +15554,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15277
15554
  code: string().min(1).max(2e4)
15278
15555
  })
15279
15556
  ]);
15557
+ /**
15558
+ * How a trigger that arrives while the automation is busy (or cooling down) is
15559
+ * handled. One of the automation's execution modes, never a property of its
15560
+ * actions. Absent means `single`.
15561
+ *
15562
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
15563
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
15564
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
15565
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
15566
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
15567
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
15568
+ * wait, never the single/queued limits.
15569
+ */
15570
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
15571
+ object({ mode: literal("single") }),
15572
+ object({ mode: literal("restart") }),
15573
+ object({
15574
+ mode: literal("queued"),
15575
+ max: number().int().min(1).max(10)
15576
+ }),
15577
+ object({
15578
+ mode: literal("parallel"),
15579
+ max: number().int().min(1).max(10)
15580
+ }),
15581
+ object({
15582
+ mode: literal("cooldown"),
15583
+ minutes: number().int().min(1).max(1440)
15584
+ })
15585
+ ]);
15280
15586
  var AutomationRecipeSchema = object({
15281
15587
  triggers: array(AutomationTriggerSchema),
15282
15588
  conditions: AutomationConditionSchema.optional(),
15283
- actions: array(AutomationActionSchema)
15589
+ actions: array(AutomationActionSchema),
15590
+ runMode: AutomationRunModeSchema.optional(),
15591
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
15592
+ keepTraces: number().int().min(1).max(100).optional()
15593
+ });
15594
+ new Set([
15595
+ {
15596
+ id: "person",
15597
+ name: "Person"
15598
+ },
15599
+ {
15600
+ id: "vehicle",
15601
+ name: "Vehicle"
15602
+ },
15603
+ {
15604
+ id: "animal",
15605
+ name: "Animal"
15606
+ },
15607
+ {
15608
+ id: "package",
15609
+ name: "Package"
15610
+ }
15611
+ ].map((l) => l.id));
15612
+ var COCO_TO_MACRO = {
15613
+ mapping: {
15614
+ person: "person",
15615
+ bicycle: "vehicle",
15616
+ car: "vehicle",
15617
+ motorcycle: "vehicle",
15618
+ airplane: "vehicle",
15619
+ bus: "vehicle",
15620
+ train: "vehicle",
15621
+ truck: "vehicle",
15622
+ boat: "vehicle",
15623
+ bird: "animal",
15624
+ cat: "animal",
15625
+ dog: "animal",
15626
+ horse: "animal",
15627
+ sheep: "animal",
15628
+ cow: "animal",
15629
+ elephant: "animal",
15630
+ bear: "animal",
15631
+ zebra: "animal",
15632
+ giraffe: "animal",
15633
+ suitcase: "package",
15634
+ backpack: "package",
15635
+ handbag: "package"
15636
+ },
15637
+ preserveOriginal: false
15638
+ };
15639
+ new Set(Object.values(COCO_TO_MACRO.mapping));
15640
+ /**
15641
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
15642
+ * so the editor can paint every block green or red with its reason.
15643
+ *
15644
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
15645
+ * every condition node (containers and leaves, including the ones a
15646
+ * short-circuit never reached), the run-mode verdict and every action step —
15647
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
15648
+ * for an Action run). Skipped
15649
+ * evaluations are traced too, and identical consecutive skips collapse into a
15650
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
15651
+ *
15652
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
15653
+ * vocabulary so an Action run records the same step shape.
15654
+ */
15655
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
15656
+ var TraceValueSchema = union([
15657
+ string(),
15658
+ number(),
15659
+ boolean(),
15660
+ _null(),
15661
+ array(union([string(), number()]))
15662
+ ]);
15663
+ var AutomationRunOutcomeSchema = _enum([
15664
+ "ran",
15665
+ "skipped-trigger",
15666
+ "skipped-conditions",
15667
+ "skipped-cooldown",
15668
+ "skipped-overlap",
15669
+ "failed",
15670
+ "aborted"
15671
+ ]);
15672
+ /** `not-evaluated` = a short-circuit never reached the node. */
15673
+ var TraceNodeOutcomeSchema = _enum([
15674
+ "pass",
15675
+ "fail",
15676
+ "not-evaluated"
15677
+ ]);
15678
+ var AutomationTriggerTraceSchema = object({
15679
+ kind: string().min(1),
15680
+ outcome: _enum(["pass", "fail"]),
15681
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
15682
+ reason: string(),
15683
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
15684
+ vars: record(string(), TraceValueSchema)
15685
+ });
15686
+ var AutomationConditionTraceNodeSchema = object({
15687
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
15688
+ path: string().min(1),
15689
+ kind: _enum([
15690
+ "all",
15691
+ "any",
15692
+ "not",
15693
+ "condition",
15694
+ "expression",
15695
+ "stationary-present"
15696
+ ]),
15697
+ outcome: TraceNodeOutcomeSchema,
15698
+ reason: string(),
15699
+ deviceId: number().int().optional(),
15700
+ cap: string().optional(),
15701
+ fieldPath: string().optional(),
15702
+ operator: string().optional(),
15703
+ expected: TraceValueSchema.optional(),
15704
+ /** What the mirror held; `null` when it held nothing (unknown). */
15705
+ observed: TraceValueSchema.optional()
15706
+ });
15707
+ var AutomationRunVerdictSchema = object({
15708
+ mode: AutomationRunModeSchema,
15709
+ decision: _enum([
15710
+ "run",
15711
+ "queued",
15712
+ "restart",
15713
+ "drop",
15714
+ "cooldown",
15715
+ "bypass"
15716
+ ]),
15717
+ reason: string()
15718
+ });
15719
+ /** One executed (or not-run) step — shared by automations and Action runs. */
15720
+ var AutomationStepTraceSchema = object({
15721
+ /** `actions.<i>` */
15722
+ path: string().min(1),
15723
+ kind: string().min(1),
15724
+ cap: string().optional(),
15725
+ method: string().optional(),
15726
+ deviceId: number().int().optional(),
15727
+ startedAt: number().optional(),
15728
+ endedAt: number().optional(),
15729
+ outcome: _enum([
15730
+ "ok",
15731
+ "error",
15732
+ "not-run"
15733
+ ]),
15734
+ message: string().optional()
15735
+ });
15736
+ var AutomationRunTraceSchema = object({
15737
+ runId: string().min(1),
15738
+ startedAt: number(),
15739
+ endedAt: number(),
15740
+ trigger: AutomationTriggerTraceSchema,
15741
+ conditions: array(AutomationConditionTraceNodeSchema),
15742
+ verdict: AutomationRunVerdictSchema.optional(),
15743
+ steps: array(AutomationStepTraceSchema),
15744
+ outcome: AutomationRunOutcomeSchema,
15745
+ /** How many identical consecutive skips this trace stands for (>= 1). */
15746
+ repeat: number().int().min(1),
15747
+ /** When the latest of those repeats happened. */
15748
+ lastAt: number()
15749
+ });
15750
+ /**
15751
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
15752
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
15753
+ */
15754
+ var AutomationStateDocSchema = object({
15755
+ version: literal(1),
15756
+ lastRunStartedAt: number().nullable(),
15757
+ traces: array(AutomationRunTraceSchema)
15758
+ });
15759
+ /**
15760
+ * Actions — named, runnable things an operator (or an automation) can start,
15761
+ * declared as LIST ENTRIES, never as devices.
15762
+ *
15763
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
15764
+ * hub's `actions` record kind) or declared by an addon through the
15765
+ * `action-source` collection (a Home Assistant script, a scene …). The one
15766
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
15767
+ * catalog of its own (D3) — every `list` asks the providers live.
15768
+ *
15769
+ * This file is the data half: the entry schema, the variables an entry may
15770
+ * declare, and the pure resolution of the values a run was given. It holds no
15771
+ * runtime.
15772
+ */
15773
+ /** What a variable's value is. `select` takes one of `options`. */
15774
+ var ActionVariableTypeSchema = _enum([
15775
+ "string",
15776
+ "number",
15777
+ "boolean",
15778
+ "select"
15779
+ ]);
15780
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
15781
+ var ActionVariableValueSchema = union([
15782
+ string(),
15783
+ number(),
15784
+ boolean()
15785
+ ]);
15786
+ /**
15787
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
15788
+ * value) or `{{vars.name}}` (inside a string) in their args.
15789
+ */
15790
+ var ActionVariableSchema = object({
15791
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
15792
+ label: string().min(1).max(120).optional(),
15793
+ type: ActionVariableTypeSchema,
15794
+ required: boolean().optional(),
15795
+ default: ActionVariableValueSchema.optional(),
15796
+ /** The allowed values of a `select`. */
15797
+ options: array(string().min(1)).min(1).optional()
15798
+ }).superRefine((v, ctx) => {
15799
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
15800
+ code: "custom",
15801
+ message: "a select variable needs options",
15802
+ path: ["options"]
15803
+ });
15804
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
15805
+ code: "custom",
15806
+ message: variableValueError(v, v.default) ?? "invalid default",
15807
+ path: ["default"]
15808
+ });
15809
+ });
15810
+ /** Why `value` is not a legal value for the variable, or null when it is. */
15811
+ function variableValueError(variable, value) {
15812
+ switch (variable.type) {
15813
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
15814
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
15815
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
15816
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
15817
+ }
15818
+ }
15819
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
15820
+ function isInternalActionId(actionId) {
15821
+ return actionId.startsWith("internal:") && actionId.length > 9;
15822
+ }
15823
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
15824
+ function addonIdOfActionId(actionId) {
15825
+ if (isInternalActionId(actionId)) return null;
15826
+ const at = actionId.indexOf(":");
15827
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
15828
+ }
15829
+ var ActionStatusSchema = object({
15830
+ isRunning: boolean(),
15831
+ /** Ms epoch the latest run STARTED; null when it never ran. */
15832
+ lastRunAt: number().nullable(),
15833
+ /** The latest run's failure; null on success or when it never ran. */
15834
+ lastError: string().nullable()
15835
+ });
15836
+ var ActionEntrySchema = object({
15837
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
15838
+ id: string().min(3),
15839
+ name: string().min(1).max(120),
15840
+ /** The addon that declared it; `core-blocks` for an internal one. */
15841
+ addonId: string().min(1),
15842
+ source: ActionSourceKindSchema,
15843
+ /** An addon-declared entry is edited where it was declared. */
15844
+ readOnly: boolean(),
15845
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
15846
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
15847
+ variables: array(ActionVariableSchema).max(20).optional(),
15848
+ status: ActionStatusSchema
15849
+ }).superRefine((entry, ctx) => {
15850
+ if (entry.source === "internal") {
15851
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
15852
+ code: "custom",
15853
+ message: "an internal action id is internal:<uuid>",
15854
+ path: ["id"]
15855
+ });
15856
+ return;
15857
+ }
15858
+ if (entry.steps !== void 0) ctx.addIssue({
15859
+ code: "custom",
15860
+ message: "only an internal action carries steps",
15861
+ path: ["steps"]
15862
+ });
15863
+ if (!entry.readOnly) ctx.addIssue({
15864
+ code: "custom",
15865
+ message: "an addon action is read-only here",
15866
+ path: ["readOnly"]
15867
+ });
15868
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
15869
+ code: "custom",
15870
+ message: "an addon action id is <addonId>:<localId>",
15871
+ path: ["id"]
15872
+ });
15873
+ });
15874
+ /** What an operator writes for an internal action. */
15875
+ var ActionInputSchema = object({
15876
+ name: string().min(1).max(120),
15877
+ steps: array(ActionStepSchema).min(1).max(50),
15878
+ variables: array(ActionVariableSchema).max(20).optional()
15879
+ });
15880
+ /** Who started a run. Absent = the operator pressed it. */
15881
+ var ActionRunOriginSchema = object({
15882
+ /** The automation block whose step started it. */
15883
+ blockId: string().min(1).optional() });
15884
+ /** `started` = the caller did not ask to wait. */
15885
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
15886
+ var ActionRunResultSchema = object({
15887
+ runId: string().min(1),
15888
+ outcome: ActionRunOutcomeSchema,
15889
+ /** Why it did not run, or what failed. */
15890
+ message: string().optional()
15891
+ });
15892
+ _enum([
15893
+ "not-found",
15894
+ "read-only",
15895
+ "invalid-variables",
15896
+ "invalid-steps",
15897
+ "not-linked",
15898
+ "busy"
15899
+ ]);
15900
+ object({
15901
+ actionId: string(),
15902
+ runId: string(),
15903
+ isRunning: boolean(),
15904
+ /** Set when the run ended. */
15905
+ outcome: AutomationRunOutcomeSchema.optional(),
15906
+ lastRunAt: number().nullable(),
15907
+ lastError: string().nullable()
15284
15908
  });
15285
15909
  /**
15286
15910
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -16875,7 +17499,22 @@ var BrokerProviderInfoSchema = object({
16875
17499
  */
16876
17500
  interactiveConnectPath: string().min(1).optional(),
16877
17501
  /** Relative hub path to disconnect the linked account (same popup pattern). */
16878
- interactiveDisconnectPath: string().min(1).optional()
17502
+ interactiveDisconnectPath: string().min(1).optional(),
17503
+ /**
17504
+ * The kind signs in through a server-held interactive session instead of
17505
+ * (or before) typed credentials. `basePath` is a hub-relative route set
17506
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
17507
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
17508
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
17509
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
17510
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
17511
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
17512
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
17513
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
17514
+ * offers it as the default path; the fields the session fills are
17515
+ * marked `filledBySignIn` in the kind's settings schema.
17516
+ */
17517
+ signIn: object({ basePath: string().min(1) }).optional()
16879
17518
  }))
16880
17519
  });
16881
17520
  var ListInputSchema = object({
@@ -17952,7 +18591,7 @@ var CompositionEventPlanSchema = object({
17952
18591
  stateField: string(),
17953
18592
  fieldKey: string()
17954
18593
  });
17955
- /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
18594
+ /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
17956
18595
  var CompositionProblemSchema = object({
17957
18596
  code: _enum([
17958
18597
  "reserved-variant",
@@ -17980,7 +18619,8 @@ var CompositionProblemSchema = object({
17980
18619
  "native-capability-replace",
17981
18620
  "mixed-quantity",
17982
18621
  "helper-mismatch",
17983
- "member-constraint"
18622
+ "member-constraint",
18623
+ "retention"
17984
18624
  ]),
17985
18625
  path: string(),
17986
18626
  message: string()
@@ -18214,13 +18854,17 @@ Object.freeze({
18214
18854
  latchedAt: {
18215
18855
  kind: "fixed",
18216
18856
  result: ["number", "null"]
18857
+ },
18858
+ schmitt: {
18859
+ kind: "fixed",
18860
+ result: ["boolean", "null"]
18217
18861
  }
18218
18862
  });
18219
18863
  /**
18220
18864
  * One catalog row per base reading a group helper can build (D944 §3).
18221
18865
  * Data, not a form default written into the block.
18222
18866
  */
18223
- var NOW = {
18867
+ var NOW$2 = {
18224
18868
  fieldPath: "lastFetchedAt",
18225
18869
  expr: "now"
18226
18870
  };
@@ -18234,6 +18878,8 @@ function numeric(input) {
18234
18878
  family: "numeric",
18235
18879
  deviceType: DeviceType.Sensor,
18236
18880
  memberDeviceType: DeviceType.Sensor,
18881
+ memberDeviceTypes: [DeviceType.Sensor],
18882
+ activeWhen: void 0,
18237
18883
  commandCaps: [],
18238
18884
  baseUnit: baseUnitOf(input.quantity),
18239
18885
  aggregates: NUMERIC_AGGREGATES,
@@ -18249,6 +18895,8 @@ function binary(input) {
18249
18895
  family: "binary",
18250
18896
  deviceType: DeviceType.Sensor,
18251
18897
  memberDeviceType: DeviceType.Sensor,
18898
+ memberDeviceTypes: [DeviceType.Sensor],
18899
+ activeWhen: void 0,
18252
18900
  commandCaps: [],
18253
18901
  quantity: "none",
18254
18902
  baseUnit: void 0,
@@ -18262,73 +18910,73 @@ var GROUP_TEMPLATES = [
18262
18910
  summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18263
18911
  role: DeviceRole.TemperatureSensor,
18264
18912
  outputCap: "temperature-sensor",
18265
- outputField: "celsius",
18913
+ outputField: "temperature",
18266
18914
  quantity: "temperature",
18267
18915
  defaultAggregate: "mean",
18268
18916
  empty: "null",
18269
- companions: [NOW]
18917
+ companions: [NOW$2]
18270
18918
  }),
18271
18919
  numeric({
18272
18920
  id: "humidity",
18273
18921
  summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18274
18922
  role: DeviceRole.HumiditySensor,
18275
18923
  outputCap: "humidity-sensor",
18276
- outputField: "percent",
18924
+ outputField: "humidity",
18277
18925
  quantity: "humidity",
18278
18926
  defaultAggregate: "mean",
18279
18927
  empty: "null",
18280
- companions: [NOW]
18928
+ companions: [NOW$2]
18281
18929
  }),
18282
18930
  numeric({
18283
18931
  id: "pressure",
18284
18932
  summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18285
18933
  role: DeviceRole.PressureSensor,
18286
18934
  outputCap: "pressure-sensor",
18287
- outputField: "hpa",
18935
+ outputField: "pressure",
18288
18936
  quantity: "pressure",
18289
18937
  defaultAggregate: "mean",
18290
18938
  empty: "null",
18291
- companions: [NOW]
18939
+ companions: [NOW$2]
18292
18940
  }),
18293
18941
  numeric({
18294
18942
  id: "illuminance",
18295
18943
  summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18296
18944
  role: DeviceRole.AmbientLightSensor,
18297
18945
  outputCap: "ambient-light-sensor",
18298
- outputField: "lux",
18946
+ outputField: "illuminance",
18299
18947
  quantity: "illuminance",
18300
18948
  defaultAggregate: "mean",
18301
18949
  empty: "null",
18302
- companions: [NOW]
18950
+ companions: [NOW$2]
18303
18951
  }),
18304
18952
  numeric({
18305
18953
  id: "power",
18306
18954
  summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18307
18955
  role: DeviceRole.PowerSensor,
18308
18956
  outputCap: "power-meter",
18309
- outputField: "watts",
18957
+ outputField: "power",
18310
18958
  quantity: "power",
18311
18959
  defaultAggregate: "sum",
18312
18960
  empty: "omit",
18313
- companions: [NOW]
18961
+ companions: [NOW$2]
18314
18962
  }),
18315
18963
  numeric({
18316
18964
  id: "energy",
18317
18965
  summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18318
18966
  role: DeviceRole.EnergySensor,
18319
18967
  outputCap: "power-meter",
18320
- outputField: "kwhTotal",
18968
+ outputField: "energy",
18321
18969
  quantity: "energy",
18322
18970
  defaultAggregate: "sum",
18323
18971
  empty: "omit",
18324
- companions: [NOW]
18972
+ companions: [NOW$2]
18325
18973
  }),
18326
18974
  numeric({
18327
18975
  id: "battery",
18328
18976
  summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18329
18977
  role: DeviceRole.BatterySensor,
18330
18978
  outputCap: "battery",
18331
- outputField: "percentage",
18979
+ outputField: "level",
18332
18980
  quantity: "percentage",
18333
18981
  defaultAggregate: "mean",
18334
18982
  empty: "null",
@@ -18356,7 +19004,7 @@ var GROUP_TEMPLATES = [
18356
19004
  quantity: "device",
18357
19005
  defaultAggregate: "mean",
18358
19006
  empty: "null",
18359
- companions: [NOW]
19007
+ companions: [NOW$2]
18360
19008
  }),
18361
19009
  binary({
18362
19010
  id: "motion",
@@ -18450,6 +19098,11 @@ var GROUP_TEMPLATES = [
18450
19098
  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.",
18451
19099
  deviceType: DeviceType.Switch,
18452
19100
  outputCap: "switch",
19101
+ outputField: "on",
19102
+ member: {
19103
+ cap: "switch",
19104
+ fieldPath: "on"
19105
+ },
18453
19106
  commandCaps: ["switch"]
18454
19107
  }),
18455
19108
  actuator({
@@ -18457,34 +19110,136 @@ var GROUP_TEMPLATES = [
18457
19110
  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.",
18458
19111
  deviceType: DeviceType.Light,
18459
19112
  outputCap: "switch",
19113
+ outputField: "on",
19114
+ member: {
19115
+ cap: "switch",
19116
+ fieldPath: "on"
19117
+ },
18460
19118
  commandCaps: [
18461
19119
  "switch",
18462
19120
  "brightness",
18463
19121
  "color"
18464
19122
  ]
19123
+ }),
19124
+ actuator({
19125
+ id: "fan",
19126
+ 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.",
19127
+ deviceType: DeviceType.Fan,
19128
+ outputCap: "switch",
19129
+ outputField: "on",
19130
+ member: {
19131
+ cap: "switch",
19132
+ fieldPath: "on"
19133
+ },
19134
+ commandCaps: ["switch", "fan-control"]
19135
+ }),
19136
+ actuator({
19137
+ id: "cover",
19138
+ 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.",
19139
+ deviceType: DeviceType.Cover,
19140
+ outputCap: "group-active",
19141
+ outputField: "active",
19142
+ member: {
19143
+ cap: "cover",
19144
+ fieldPath: "state"
19145
+ },
19146
+ activeWhen: [
19147
+ "open",
19148
+ "opening",
19149
+ "stopped"
19150
+ ],
19151
+ commandCaps: ["cover"]
19152
+ }),
19153
+ actuator({
19154
+ id: "valve",
19155
+ 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.",
19156
+ deviceType: DeviceType.Valve,
19157
+ outputCap: "group-active",
19158
+ outputField: "active",
19159
+ member: {
19160
+ cap: "valve",
19161
+ fieldPath: "state"
19162
+ },
19163
+ activeWhen: [
19164
+ "open",
19165
+ "opening",
19166
+ "stopped"
19167
+ ],
19168
+ commandCaps: ["valve"]
19169
+ }),
19170
+ actuator({
19171
+ id: "lock",
19172
+ 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.",
19173
+ deviceType: DeviceType.Lock,
19174
+ outputCap: "group-active",
19175
+ outputField: "active",
19176
+ member: {
19177
+ cap: "lock-control",
19178
+ fieldPath: "state"
19179
+ },
19180
+ activeWhen: ["locked"],
19181
+ commandCaps: ["lock-control"]
19182
+ }),
19183
+ actuator({
19184
+ id: "media-player",
19185
+ 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.",
19186
+ deviceType: DeviceType.MediaPlayer,
19187
+ outputCap: "group-active",
19188
+ outputField: "active",
19189
+ member: {
19190
+ cap: "media-player",
19191
+ fieldPath: "state"
19192
+ },
19193
+ activeWhen: ["playing", "buffering"],
19194
+ commandCaps: ["media-player"]
19195
+ }),
19196
+ actuator({
19197
+ id: "climate",
19198
+ 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.",
19199
+ deviceType: DeviceType.Thermostat,
19200
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
19201
+ outputCap: "group-active",
19202
+ outputField: "active",
19203
+ member: {
19204
+ cap: "climate-control",
19205
+ fieldPath: "mode"
19206
+ },
19207
+ activeWhen: [
19208
+ "heat",
19209
+ "cool",
19210
+ "auto",
19211
+ "heat_cool",
19212
+ "fan_only",
19213
+ "dry"
19214
+ ],
19215
+ commandCaps: ["climate-control"]
18465
19216
  })
18466
19217
  ];
18467
19218
  function actuator(input) {
18468
19219
  return {
18469
- ...input,
19220
+ id: input.id,
19221
+ summary: input.summary,
19222
+ deviceType: input.deviceType,
19223
+ outputCap: input.outputCap,
19224
+ outputField: input.outputField,
19225
+ commandCaps: input.commandCaps,
18470
19226
  family: "actuator",
18471
19227
  role: void 0,
18472
19228
  memberDeviceType: input.deviceType,
18473
- outputField: "on",
19229
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
19230
+ activeWhen: input.activeWhen,
18474
19231
  quantity: "none",
18475
19232
  baseUnit: void 0,
18476
19233
  aggregates: BINARY_AGGREGATES,
18477
19234
  defaultAggregate: "any",
18478
19235
  empty: "offline",
18479
- accept: [{
18480
- cap: "switch",
18481
- fieldPath: "on"
18482
- }],
19236
+ accept: [input.member],
18483
19237
  companions: [CHANGED]
18484
19238
  };
18485
19239
  }
18486
19240
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
18487
19241
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
19242
+ DeviceType.Camera;
18488
19243
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
18489
19244
  /**
18490
19245
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -18799,7 +19554,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
18799
19554
  kind: "mutation",
18800
19555
  auth: "admin",
18801
19556
  access: "view"
18802
- });
19557
+ }), method(object({
19558
+ blockId: string().min(1),
19559
+ trace: AutomationRunTraceSchema.optional(),
19560
+ runStartedAt: number().optional(),
19561
+ keepTraces: number().int().min(1).max(100).optional()
19562
+ }), _void(), {
19563
+ kind: "mutation",
19564
+ auth: "admin"
19565
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
19566
+ traces: array(AutomationRunTraceSchema),
19567
+ lastError: string().nullable()
19568
+ }), { auth: "admin" });
18803
19569
  /**
18804
19570
  * `custom-model-registry` — collection cap exposing operator-registered
18805
19571
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -43804,7 +44570,7 @@ var NetworkAccessStatusSchema = object({
43804
44570
  });
43805
44571
  /**
43806
44572
  * Optional, richer endpoint shape returned by providers that expose
43807
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
44573
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
43808
44574
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
43809
44575
  * the originating provider config (mode + sourcePort) so the
43810
44576
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -44197,51 +44963,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
44197
44963
  kind: "mutation",
44198
44964
  auth: "admin"
44199
44965
  });
44200
- new Set([
44201
- {
44202
- id: "person",
44203
- name: "Person"
44204
- },
44205
- {
44206
- id: "vehicle",
44207
- name: "Vehicle"
44208
- },
44209
- {
44210
- id: "animal",
44211
- name: "Animal"
44212
- },
44213
- {
44214
- id: "package",
44215
- name: "Package"
44216
- }
44217
- ].map((l) => l.id));
44218
- var COCO_TO_MACRO = {
44219
- mapping: {
44220
- person: "person",
44221
- bicycle: "vehicle",
44222
- car: "vehicle",
44223
- motorcycle: "vehicle",
44224
- airplane: "vehicle",
44225
- bus: "vehicle",
44226
- train: "vehicle",
44227
- truck: "vehicle",
44228
- boat: "vehicle",
44229
- bird: "animal",
44230
- cat: "animal",
44231
- dog: "animal",
44232
- horse: "animal",
44233
- sheep: "animal",
44234
- cow: "animal",
44235
- elephant: "animal",
44236
- bear: "animal",
44237
- zebra: "animal",
44238
- giraffe: "animal",
44239
- suitcase: "package",
44240
- backpack: "package",
44241
- handbag: "package"
44242
- },
44243
- preserveOriginal: false
44244
- };
44245
44966
  var AUDIO_MACRO_LABELS = [
44246
44967
  {
44247
44968
  id: "speech",
@@ -46491,7 +47212,20 @@ var NcRuleInputSchema = object({
46491
47212
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
46492
47213
  * path does not run Zod defaults.
46493
47214
  */
46494
- groupingSec: number().int().min(5).max(60).optional()
47215
+ groupingSec: number().int().min(5).max(60).optional(),
47216
+ /**
47217
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47218
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47219
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47220
+ * same mark the Library writes, and records whose notification it was and
47221
+ * when. A channel with no buttons gets the same two choices as links.
47222
+ *
47223
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47224
+ * cap path): absent and `false` both mean off, which is what every rule
47225
+ * authored before this field meant. An editor turns it off by writing
47226
+ * `false`, so it needs no `clear` key.
47227
+ */
47228
+ askVerdict: boolean().optional()
46495
47229
  });
46496
47230
  /**
46497
47231
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -56795,9 +57529,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
56795
57529
  */
56796
57530
  var AirQualitySensorStatusSchema = object({
56797
57531
  /** Carbon dioxide concentration in ppm. */
56798
- co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
57532
+ co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
56799
57533
  /** Total volatile organic compounds in ppb. */
56800
- vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57534
+ voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
56801
57535
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
56802
57536
  pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
56803
57537
  /** Particulate matter ≤ 10 μm in µg/m³. */
@@ -56830,12 +57564,12 @@ var airQualitySensorCapability = {
56830
57564
  * History (D850): recorded series.
56831
57565
  */
56832
57566
  history: { fields: {
56833
- co2Ppm: {
57567
+ co2: {
56834
57568
  kind: "gauge",
56835
57569
  unit: "ppm",
56836
57570
  deadband: 5
56837
57571
  },
56838
- vocPpb: {
57572
+ voc: {
56839
57573
  kind: "gauge",
56840
57574
  unit: "ppb",
56841
57575
  deadband: 5
@@ -56866,7 +57600,7 @@ var airQualitySensorCapability = {
56866
57600
  */
56867
57601
  var AmbientLightSensorStatusSchema = object({
56868
57602
  /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
56869
- lux: number().min(0).nullable().meta(readingMeta("illuminance", {
57603
+ illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
56870
57604
  precision: 0,
56871
57605
  label: "Illuminance"
56872
57606
  })),
@@ -56895,7 +57629,7 @@ var ambientLightSensorCapability = {
56895
57629
  /**
56896
57630
  * History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
56897
57631
  */
56898
- history: { fields: { lux: {
57632
+ history: { fields: { illuminance: {
56899
57633
  kind: "gauge",
56900
57634
  unit: "lx",
56901
57635
  deadbandPct: 10,
@@ -57034,6 +57768,84 @@ var audioMetricsCapability = {
57034
57768
  */
57035
57769
  history: "none"
57036
57770
  };
57771
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
57772
+ actionId: string().min(1),
57773
+ variables: record(string(), ActionVariableValueSchema).optional()
57774
+ }), _void(), {
57775
+ kind: "mutation",
57776
+ auth: "admin"
57777
+ }), method(object({ actionId: string().min(1) }), _void(), {
57778
+ kind: "mutation",
57779
+ auth: "admin"
57780
+ });
57781
+ /**
57782
+ * actions — the ONE surface for runnable actions, for the UI and for
57783
+ * automations. Provided by the core-blocks builtin on the hub.
57784
+ *
57785
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
57786
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
57787
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
57788
+ * the run status from the hub's `runs` ledger; there is no catalog here to
57789
+ * drift (D3).
57790
+ *
57791
+ * **Permissions follow the camera, as linked devices do.** An action is linked
57792
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
57793
+ * non-admin may `get` / `run` an action only through a camera they may see that
57794
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
57795
+ * gate sees it) and the provider refuses an action that camera does not link.
57796
+ * An admin, and an in-process caller (an automation block), may run any action.
57797
+ * Every refusal is typed (`[not-linked] …`) and logged.
57798
+ *
57799
+ * A second `run` of an action that is already running is REFUSED — traced and
57800
+ * logged — never queued.
57801
+ */
57802
+ var ActionIdInput = object({ actionId: string().min(1) });
57803
+ var ActionLinksSchema = object({
57804
+ deviceId: number().int().nonnegative(),
57805
+ actionIds: array(string().min(1)).max(100)
57806
+ });
57807
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
57808
+ actionId: string().min(1),
57809
+ deviceId: number().int().nonnegative().optional()
57810
+ }), object({ action: ActionEntrySchema.nullable() }), {
57811
+ role: "viewer",
57812
+ auth: "protected",
57813
+ kind: "query",
57814
+ caller: "required"
57815
+ }), method(object({
57816
+ actionId: string().min(1),
57817
+ variables: record(string(), ActionVariableValueSchema).optional(),
57818
+ wait: boolean().optional(),
57819
+ origin: ActionRunOriginSchema.optional(),
57820
+ deviceId: number().int().nonnegative().optional()
57821
+ }), ActionRunResultSchema, {
57822
+ role: "viewer",
57823
+ auth: "protected",
57824
+ kind: "mutation",
57825
+ caller: "required"
57826
+ }), method(ActionIdInput, _void(), {
57827
+ kind: "mutation",
57828
+ auth: "admin"
57829
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
57830
+ kind: "mutation",
57831
+ auth: "admin"
57832
+ }), method(object({
57833
+ actionId: string().min(1),
57834
+ action: ActionInputSchema.partial()
57835
+ }), object({ action: ActionEntrySchema }), {
57836
+ kind: "mutation",
57837
+ auth: "admin"
57838
+ }), method(ActionIdInput, _void(), {
57839
+ kind: "mutation",
57840
+ auth: "admin"
57841
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
57842
+ role: "viewer",
57843
+ auth: "protected",
57844
+ kind: "query"
57845
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
57846
+ kind: "mutation",
57847
+ auth: "admin"
57848
+ });
57037
57849
  /**
57038
57850
  * Automation-control cap. Models HA `automation.*` entities on
57039
57851
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -57130,7 +57942,7 @@ var BatteryStatusSchema = object({
57130
57942
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
57131
57943
  * declines to band a non-finite reading, which is the correct shape.
57132
57944
  */
57133
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
57945
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
57134
57946
  /**
57135
57947
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
57136
57948
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -57140,7 +57952,7 @@ var BatteryStatusSchema = object({
57140
57952
  * `'unknown'` is the absence of an answer: the provider has not read the
57141
57953
  * power fields yet, or the firmware reported neither. Before it existed,
57142
57954
  * two unreadable fields produced `'none'`, and a camera nobody had reached
57143
- * was drawn "on battery alone" — the same D315 defect `percentage` closed
57955
+ * was drawn "on battery alone" — the same D315 defect `level` closed
57144
57956
  * by going nullable. Consumers SKIP it (no charger state published, no
57145
57957
  * glyph, no HomeKit ChargingState) rather than coerce it either way.
57146
57958
  */
@@ -57164,7 +57976,7 @@ var BatteryStatusSchema = object({
57164
57976
  * that only report charge. A second reading on this cap, not a second
57165
57977
  * `numeric-sensor` on the same device.
57166
57978
  */
57167
- volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
57979
+ voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
57168
57980
  /**
57169
57981
  * Ms epoch of the last time the device PROVED it was reachable — a
57170
57982
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -57198,7 +58010,7 @@ var BatteryStatusSchema = object({
57198
58010
  /**
57199
58011
  * True when the source is a BINARY low-battery indicator (HA
57200
58012
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
57201
- * charge level — `percentage` is then a coarse stand-in (100 = normal,
58013
+ * charge level — `level` is then a coarse stand-in (100 = normal,
57202
58014
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
57203
58015
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
57204
58016
  */
@@ -57211,21 +58023,22 @@ var BATTERY_DEVICE_TYPES = [
57211
58023
  DeviceType.Switch,
57212
58024
  DeviceType.PetFeeder,
57213
58025
  DeviceType.Container,
57214
- DeviceType.Thermostat
58026
+ DeviceType.Thermostat,
58027
+ DeviceType.EventEmitter
57215
58028
  ];
57216
58029
  /** Generic Customize target for the percentage reading (D936). */
57217
58030
  var batteryCustomize = {
57218
58031
  version: 1,
57219
58032
  targets: [{
57220
- id: "percentage",
58033
+ id: "level",
57221
58034
  cardinality: "scalar",
57222
58035
  vocabulary: {
57223
58036
  cap: "battery",
57224
- stateField: "percentage"
58037
+ stateField: "level"
57225
58038
  },
57226
58039
  deviceTypes: BATTERY_DEVICE_TYPES,
57227
58040
  apply: "add",
57228
- path: "percentage",
58041
+ path: "level",
57229
58042
  sources: [{
57230
58043
  kind: "field",
57231
58044
  valueKinds: ["number", "string"],
@@ -57319,7 +58132,7 @@ onStatusChanged: { data: object({
57319
58132
  schema: BatteryStatusSchema,
57320
58133
  kind: "push",
57321
58134
  empty: {
57322
- percentage: null,
58135
+ level: null,
57323
58136
  charging: "unknown",
57324
58137
  sleeping: false,
57325
58138
  lastUpdated: 0
@@ -57346,11 +58159,11 @@ onStatusChanged: { data: object({
57346
58159
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
57347
58160
  */
57348
58161
  history: { fields: {
57349
- percentage: {
58162
+ level: {
57350
58163
  kind: "gauge",
57351
58164
  unit: "%"
57352
58165
  },
57353
- volts: {
58166
+ voltage: {
57354
58167
  kind: "gauge",
57355
58168
  unit: "V"
57356
58169
  }
@@ -57410,14 +58223,14 @@ var binaryCapability = {
57410
58223
  * controls to this single-method surface.
57411
58224
  *
57412
58225
  * The cap is intentionally minimal: a single `setBrightness({deviceId,
57413
- * percentage})` mutation plus the auto-injected `getStatus`. Drivers
58226
+ * level})` mutation plus the auto-injected `getStatus`. Drivers
57414
58227
  * that expose richer controls (color temperature, scenes, schedules)
57415
58228
  * should surface those via the device's `getSettingsUISchema()`
57416
58229
  * instead of bloating this cap.
57417
58230
  */
57418
58231
  var BrightnessStatusSchema = object({
57419
58232
  /** Current level as 0..100 inclusive. Firmware-reported. */
57420
- percentage: number().min(0).max(100).meta(readingMeta("percentage")),
58233
+ level: number().min(0).max(100).meta(readingMeta("percentage")),
57421
58234
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
57422
58235
  lastChangedAt: number()
57423
58236
  });
@@ -57429,7 +58242,7 @@ var brightnessCapability = {
57429
58242
  deviceTypes: [DeviceType.Light],
57430
58243
  methods: { setBrightness: method(object({
57431
58244
  deviceId: number().int().nonnegative(),
57432
- percentage: number().min(0).max(100)
58245
+ level: number().min(0).max(100)
57433
58246
  }), _void(), {
57434
58247
  kind: "mutation",
57435
58248
  auth: "protected"
@@ -57442,7 +58255,7 @@ var brightnessCapability = {
57442
58255
  */
57443
58256
  onBrightnessChanged: { data: object({
57444
58257
  deviceId: number(),
57445
- percentage: number().min(0).max(100),
58258
+ level: number().min(0).max(100),
57446
58259
  lastChangedAt: number()
57447
58260
  }) } },
57448
58261
  status: {
@@ -57465,7 +58278,7 @@ onBrightnessChanged: { data: object({
57465
58278
  /**
57466
58279
  * History (D850): recorded series. Actuator tier: level keeps every change.
57467
58280
  */
57468
- history: { fields: { percentage: {
58281
+ history: { fields: { level: {
57469
58282
  kind: "gauge",
57470
58283
  unit: "%",
57471
58284
  minIntervalMs: 0
@@ -58079,11 +58892,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
58079
58892
  }),
58080
58893
  object({
58081
58894
  mode: literal("mired"),
58082
- mireds: number().int().min(50).max(1e3)
58895
+ colorTemperature: number().int().min(50).max(1e3)
58083
58896
  })
58084
58897
  ]);
58085
58898
  var ColorStatusSchema = object({
58086
- /** Active color mode — which of `rgb` / `hsv` / `mireds` reflects
58899
+ /** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
58087
58900
  * the bulb's current state. */
58088
58901
  mode: _enum([
58089
58902
  "rgb",
@@ -58095,7 +58908,7 @@ var ColorStatusSchema = object({
58095
58908
  /** Populated when `mode === 'hsv'`. */
58096
58909
  hsv: HsvTripletSchema.optional(),
58097
58910
  /** Populated when `mode === 'mired'`. */
58098
- mireds: number().int().optional().meta(readingMeta("colour-temperature")),
58911
+ colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
58099
58912
  /** Ms epoch of the last operator-driven change. */
58100
58913
  lastChangedAt: number()
58101
58914
  });
@@ -58127,7 +58940,7 @@ onColorChanged: { data: object({
58127
58940
  ]),
58128
58941
  rgb: RgbTripletSchema.optional(),
58129
58942
  hsv: HsvTripletSchema.optional(),
58130
- mireds: number().int().optional(),
58943
+ colorTemperature: number().int().optional(),
58131
58944
  lastChangedAt: number()
58132
58945
  }) } },
58133
58946
  status: {
@@ -59004,11 +59817,11 @@ var DishwasherStatusSchema = object({
59004
59817
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
59005
59818
  availablePrograms: array(string()),
59006
59819
  /** Seconds remaining on the active cycle, or null when not running / not known. */
59007
- remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59820
+ remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59008
59821
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
59009
- delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59822
+ delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59010
59823
  /** Cycle completion 0..100, or null when not applicable. */
59011
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59824
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59012
59825
  /** True when the door is reported open; null when the door state is not known. */
59013
59826
  doorOpen: boolean().nullable(),
59014
59827
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -59227,14 +60040,14 @@ var dishwasherCapability = {
59227
60040
  runtimeState: DishwasherStatusSchema,
59228
60041
  durability: "session",
59229
60042
  history: { fields: {
59230
- progressPercent: {
60043
+ progress: {
59231
60044
  kind: "gauge",
59232
60045
  unit: "%",
59233
60046
  deadband: 2
59234
60047
  },
59235
60048
  state: { kind: "enum" },
59236
60049
  operationState: { kind: "enum" },
59237
- remainingSeconds: {
60050
+ remaining: {
59238
60051
  kind: "gauge",
59239
60052
  unit: "s",
59240
60053
  minIntervalMs: 6e4
@@ -59825,7 +60638,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
59825
60638
  var FanControlStatusSchema = object({
59826
60639
  /** Active speed as 0..100 inclusive. Null when the device has no
59827
60640
  * speed surface (single-speed fan). */
59828
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60641
+ speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59829
60642
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
59830
60643
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
59831
60644
  percentageStep: number().positive().optional().meta(readingMeta("percentage")),
@@ -59852,7 +60665,7 @@ var fanControlCapability = {
59852
60665
  methods: {
59853
60666
  setPercentage: method(object({
59854
60667
  deviceId: number().int().nonnegative(),
59855
- percentage: number().min(0).max(100)
60668
+ speed: number().min(0).max(100)
59856
60669
  }), _void(), {
59857
60670
  kind: "mutation",
59858
60671
  auth: "protected"
@@ -59885,7 +60698,7 @@ var fanControlCapability = {
59885
60698
  },
59886
60699
  /**
59887
60700
  * Runtime-state slice — mirrored by the kernel. UI fan speed
59888
- * sliders read `percentage` for live updates.
60701
+ * sliders read `speed` for live updates.
59889
60702
  */
59890
60703
  runtimeState: FanControlStatusSchema,
59891
60704
  /**
@@ -59899,7 +60712,7 @@ var fanControlCapability = {
59899
60712
  * History (D850): recorded series. Actuator tier: speed keeps every change.
59900
60713
  */
59901
60714
  history: { fields: {
59902
- percentage: {
60715
+ speed: {
59903
60716
  kind: "gauge",
59904
60717
  unit: "%",
59905
60718
  minIntervalMs: 0
@@ -60088,7 +60901,17 @@ var groupCommandsCapability = {
60088
60901
  scope: "device",
60089
60902
  deviceNative: true,
60090
60903
  mode: "singleton",
60091
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
60904
+ deviceTypes: [
60905
+ DeviceType.Switch,
60906
+ DeviceType.Light,
60907
+ DeviceType.Fan,
60908
+ DeviceType.Cover,
60909
+ DeviceType.Valve,
60910
+ DeviceType.Lock,
60911
+ DeviceType.MediaPlayer,
60912
+ DeviceType.Thermostat,
60913
+ DeviceType.Climate
60914
+ ],
60092
60915
  methods: {},
60093
60916
  status: {
60094
60917
  schema: GroupCommandsSchema,
@@ -60106,6 +60929,49 @@ var groupCommandsCapability = {
60106
60929
  history: "none"
60107
60930
  };
60108
60931
  /**
60932
+ * Whether a helper actuator group counts as active (D944): at least one
60933
+ * included member is, or every included member is. What "active" means is the
60934
+ * type's: a cover is open, a lock is locked, a media player is playing, a
60935
+ * climate device is not off. A group of lights, switches and fans reports
60936
+ * `switch.on` instead; this cap is for the types whose own state is not a
60937
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
60938
+ * empty included set takes the device offline, never `active: false`.
60939
+ */
60940
+ var GroupActiveSchema = object({
60941
+ active: boolean().meta({ label: "Active" }),
60942
+ /** Ms epoch when the slice was last updated. */
60943
+ lastChangedAt: number()
60944
+ });
60945
+ var groupActiveCapability = {
60946
+ name: "group-active",
60947
+ scope: "device",
60948
+ deviceNative: true,
60949
+ mode: "singleton",
60950
+ deviceTypes: [
60951
+ DeviceType.Cover,
60952
+ DeviceType.Valve,
60953
+ DeviceType.Lock,
60954
+ DeviceType.MediaPlayer,
60955
+ DeviceType.Climate,
60956
+ DeviceType.Thermostat
60957
+ ],
60958
+ methods: {},
60959
+ status: {
60960
+ schema: GroupActiveSchema,
60961
+ kind: "push"
60962
+ },
60963
+ runtimeState: GroupActiveSchema,
60964
+ /**
60965
+ * Runtime-state durability: **session** — recomputed from the members on the
60966
+ * next source event; a restored value would outlive the members it named.
60967
+ *
60968
+ * See `RuntimeStateDurability`. Enforced by
60969
+ * `scripts/check-runtime-state-durability.ts`.
60970
+ */
60971
+ durability: "session",
60972
+ history: "none"
60973
+ };
60974
+ /**
60109
60975
  * How many members of a helper group answered (D944). Counts are integers,
60110
60976
  * not measurements: zero included is a real count, never a stand-in reading.
60111
60977
  * The reading itself stays on the output capability. History is none — the
@@ -60136,7 +61002,14 @@ var groupStatusCapability = {
60136
61002
  deviceTypes: [
60137
61003
  DeviceType.Sensor,
60138
61004
  DeviceType.Switch,
60139
- DeviceType.Light
61005
+ DeviceType.Light,
61006
+ DeviceType.Fan,
61007
+ DeviceType.Cover,
61008
+ DeviceType.Valve,
61009
+ DeviceType.Lock,
61010
+ DeviceType.MediaPlayer,
61011
+ DeviceType.Thermostat,
61012
+ DeviceType.Climate
60140
61013
  ],
60141
61014
  methods: {},
60142
61015
  status: {
@@ -60284,7 +61157,7 @@ var humidifierCapability = {
60284
61157
  */
60285
61158
  var HumiditySensorStatusSchema = object({
60286
61159
  /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
60287
- percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
61160
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
60288
61161
  precision: 0,
60289
61162
  label: "Humidity"
60290
61163
  })),
@@ -60313,7 +61186,7 @@ var humiditySensorCapability = {
60313
61186
  /**
60314
61187
  * History (D850): recorded series.
60315
61188
  */
60316
- history: { fields: { percent: {
61189
+ history: { fields: { humidity: {
60317
61190
  kind: "gauge",
60318
61191
  unit: "%",
60319
61192
  deadband: .5
@@ -60953,7 +61826,7 @@ var LawnMowerControlStatusSchema = object({
60953
61826
  batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60954
61827
  /** 0..100 mowing-completion percentage of the current task, or null when no
60955
61828
  * task is active / progress is unavailable. */
60956
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61829
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60957
61830
  /** Current device/event code (dynamic — mostly status, sometimes an error),
60958
61831
  * or null when unknown. */
60959
61832
  currentCode: number().nullable().meta(readingMeta("none")),
@@ -61409,7 +62282,14 @@ var lockControlCapability = {
61409
62282
  }),
61410
62283
  unlock: method(object({
61411
62284
  deviceId: number().int().nonnegative(),
61412
- code: string().min(1).optional()
62285
+ code: string().min(1).optional(),
62286
+ /**
62287
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62288
+ * with one call is refused unless the caller says it was confirmed. A
62289
+ * single lock ignores it. The refusal lives in the callee, so no
62290
+ * automation or API caller reaches the doors by skipping a dialog.
62291
+ */
62292
+ confirm: boolean().optional()
61413
62293
  }), _void(), {
61414
62294
  kind: "mutation",
61415
62295
  auth: "protected"
@@ -61446,6 +62326,44 @@ var lockControlCapability = {
61446
62326
  volatileStateFields: ["lastChangedAt"]
61447
62327
  };
61448
62328
  /**
62329
+ * A monotonic total between resets (D944). Energy uses `power-meter.energy`
62330
+ * so dashboards keep one series; every other instantaneous reading lands here.
62331
+ * `value` is the time integral in the source quantity's accumulated base
62332
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
62333
+ */
62334
+ var MeterStatusSchema = object({
62335
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
62336
+ lastFetchedAt: number()
62337
+ });
62338
+ var meterCapability = {
62339
+ name: "meter",
62340
+ scope: "device",
62341
+ deviceNative: true,
62342
+ mode: "singleton",
62343
+ deviceTypes: [DeviceType.Sensor],
62344
+ methods: {},
62345
+ status: {
62346
+ schema: MeterStatusSchema,
62347
+ kind: "push"
62348
+ },
62349
+ runtimeState: MeterStatusSchema,
62350
+ /**
62351
+ * Runtime-state durability: **restored** — the total is a cache of the
62352
+ * history integral. A restart shows it only until the history pass
62353
+ * recomputes the open period; it is not an in-memory buffer.
62354
+ *
62355
+ * See `RuntimeStateDurability`. Enforced by
62356
+ * `scripts/check-runtime-state-durability.ts`.
62357
+ */
62358
+ durability: "restored",
62359
+ history: { fields: { value: {
62360
+ kind: "counter",
62361
+ deadband: .001,
62362
+ minIntervalMs: 6e4
62363
+ } } },
62364
+ volatileStateFields: ["lastFetchedAt"]
62365
+ };
62366
+ /**
61449
62367
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
61450
62368
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
61451
62369
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -61502,10 +62420,10 @@ var MediaPlayerStatusSchema = object({
61502
62420
  currentMedia: MediaInfoSchema.nullable(),
61503
62421
  /** Current playback position in ms. Null when not seekable or
61504
62422
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
61505
- positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62423
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
61506
62424
  /** Total duration of the current media in ms. Null when unknown
61507
62425
  * (live stream) or nothing is playing. */
61508
- durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62426
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
61509
62427
  /** Shuffle toggle. Null when no shuffle surface.
61510
62428
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
61511
62429
  shuffle: boolean().nullable(),
@@ -61544,7 +62462,7 @@ var mediaPlayerCapability = {
61544
62462
  }),
61545
62463
  seek: method(object({
61546
62464
  deviceId: number().int().nonnegative(),
61547
- positionMs: number().int().nonnegative()
62465
+ position: number().int().nonnegative()
61548
62466
  }), _void(), {
61549
62467
  kind: "mutation",
61550
62468
  auth: "protected"
@@ -61616,7 +62534,7 @@ var mediaPlayerCapability = {
61616
62534
  /**
61617
62535
  * History (D850): volume (a level an operator or an automation sets; the
61618
62536
  * default 10 s floor folds a slider drag into its end value) and mute.
61619
- * `positionMs` is not recorded: it moves continuously while playing.
62537
+ * `position` is not recorded: it moves continuously while playing.
61620
62538
  */
61621
62539
  history: { fields: {
61622
62540
  volumeLevel: {
@@ -62578,9 +63496,9 @@ var NetworkLinkStatusSchema = object({
62578
63496
  * one whose reading has not landed must not be drawn at 0 %. Consumers
62579
63497
  * SKIP a null rather than coerce it.
62580
63498
  */
62581
- signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63499
+ signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62582
63500
  /** Raw received signal strength in dBm, when the firmware reports one. */
62583
- rssiDbm: number().optional().meta(readingMeta("signal-level")),
63501
+ rssi: number().optional().meta(readingMeta("signal-level")),
62584
63502
  /** Network name of a wireless link, when the firmware reports it. */
62585
63503
  ssid: string().optional(),
62586
63504
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
@@ -62616,7 +63534,7 @@ onStatusChanged: { data: object({
62616
63534
  kind: "push",
62617
63535
  empty: {
62618
63536
  type: "unknown",
62619
- signalPercent: null,
63537
+ signal: null,
62620
63538
  lastUpdated: 0
62621
63539
  }
62622
63540
  },
@@ -62639,13 +63557,13 @@ onStatusChanged: { data: object({
62639
63557
  * History (D850): recorded series.
62640
63558
  */
62641
63559
  history: { fields: {
62642
- signalPercent: {
63560
+ signal: {
62643
63561
  kind: "gauge",
62644
63562
  unit: "%",
62645
63563
  deadband: 3,
62646
63564
  minIntervalMs: 6e4
62647
63565
  },
62648
- rssiDbm: {
63566
+ rssi: {
62649
63567
  kind: "gauge",
62650
63568
  unit: "dBm",
62651
63569
  deadband: 2,
@@ -63390,10 +64308,20 @@ var PetFeederDeviceStatusSchema = _enum([
63390
64308
  ]);
63391
64309
  var gramsPortion = number().int().min(4).max(200);
63392
64310
  var PetFeederStatusSchema = object({
63393
- /** Food currently in the bowl (grams). Null when the device has not
63394
- * reported a reading yet. On dual-hopper models this is the combined
63395
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
64311
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
64312
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
64313
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
64314
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
64315
+ * the device has not reported yet. */
63396
64316
  foodLevel: number().nullable().meta(readingMeta("none")),
64317
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
64318
+ * feeder's own units. Null when the model reports none. */
64319
+ bowlFood: number().nullable().meta(readingMeta("none")),
64320
+ /** True while the pet is eating (`state.eating`). */
64321
+ eating: boolean(),
64322
+ /** Whether `markFoodReplenished` does anything on this model. When false
64323
+ * the client must not offer it. */
64324
+ supportsMarkRefilled: boolean(),
63397
64325
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
63398
64326
  * single-hopper models. */
63399
64327
  food1: number().nullable().meta(readingMeta("none")),
@@ -63401,8 +64329,8 @@ var PetFeederStatusSchema = object({
63401
64329
  * single-hopper models. */
63402
64330
  food2: number().nullable().meta(readingMeta("none")),
63403
64331
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
63404
- * (`device_class: problem`, on = low). True when the bowl is empty /
63405
- * below the feeder's low threshold. */
64332
+ * (`device_class: problem`, on = low). True when the tank reports empty,
64333
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
63406
64334
  lowFood: boolean(),
63407
64335
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
63408
64336
  * device has no battery reading. */
@@ -63470,7 +64398,9 @@ var petFeederCapability = {
63470
64398
  kind: "mutation",
63471
64399
  auth: "protected"
63472
64400
  }),
63473
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
64401
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
64402
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
64403
+ * change any sensor reading. */
63474
64404
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
63475
64405
  kind: "mutation",
63476
64406
  auth: "protected"
@@ -63543,6 +64473,7 @@ var petFeederCapability = {
63543
64473
  */
63544
64474
  history: { fields: {
63545
64475
  foodLevel: { kind: "gauge" },
64476
+ bowlFood: { kind: "gauge" },
63546
64477
  food1: { kind: "gauge" },
63547
64478
  food2: { kind: "gauge" },
63548
64479
  batteryPower: {
@@ -63952,27 +64883,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
63952
64883
  * in real-world devices.
63953
64884
  */
63954
64885
  var PowerMeterStatusSchema = object({
63955
- /** Instantaneous power draw in watts. */
63956
- watts: number().optional().meta(readingMeta("power", { label: "Power" })),
64886
+ /** Instantaneous power draw (watts). */
64887
+ power: number().optional().meta(readingMeta("power", { label: "Power" })),
63957
64888
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
63958
- kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
63959
- /** Voltage in volts. */
63960
- volts: number().optional().meta(readingMeta("voltage")),
64889
+ energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64890
+ /** Voltage (volts). */
64891
+ voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
63961
64892
  /** Current in amperes. */
63962
- amps: number().optional().meta(readingMeta("current")),
64893
+ current: number().optional().meta(readingMeta("current", { label: "Current" })),
63963
64894
  /** Apparent power in volt-amperes. */
63964
- va: number().optional().meta(readingMeta("apparent-power")),
64895
+ apparentPower: number().optional().meta(readingMeta("apparent-power")),
63965
64896
  /** Power factor (0..1) if reported. */
63966
64897
  powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
63967
64898
  /** Ms epoch when the slice was last updated. */
63968
64899
  lastFetchedAt: number()
63969
64900
  });
64901
+ /**
64902
+ * Loads that draw metered mains power (D951). The reading is a feature of the
64903
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
64904
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
64905
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
64906
+ * camera and the like are left out: their draw is not what anyone meters.
64907
+ */
64908
+ var POWER_METER_DEVICE_TYPES = [
64909
+ DeviceType.Sensor,
64910
+ DeviceType.Switch,
64911
+ DeviceType.Light,
64912
+ DeviceType.Fan,
64913
+ DeviceType.Climate,
64914
+ DeviceType.Thermostat,
64915
+ DeviceType.Humidifier,
64916
+ DeviceType.WaterHeater,
64917
+ DeviceType.MediaPlayer,
64918
+ DeviceType.Washer,
64919
+ DeviceType.Dryer,
64920
+ DeviceType.Dishwasher,
64921
+ DeviceType.Vacuum,
64922
+ DeviceType.LawnMower,
64923
+ DeviceType.PetFeeder
64924
+ ];
64925
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
64926
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
64927
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
64928
+ function powerTarget(id, unit) {
64929
+ return {
64930
+ id,
64931
+ cardinality: "scalar",
64932
+ vocabulary: {
64933
+ cap: "power-meter",
64934
+ stateField: id
64935
+ },
64936
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
64937
+ apply: "add",
64938
+ path: id,
64939
+ sources: [{
64940
+ kind: "numeric",
64941
+ requiredUnit: unit,
64942
+ transforms: [{ kind: "identity" }]
64943
+ }],
64944
+ companions: [{
64945
+ kind: "clock",
64946
+ path: "lastFetchedAt",
64947
+ when: "always"
64948
+ }],
64949
+ sourceReport: { status: "assignment-status" },
64950
+ advancedExpression: true
64951
+ };
64952
+ }
64953
+ /** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
64954
+ var powerMeterCustomize = {
64955
+ version: 1,
64956
+ targets: [
64957
+ powerTarget("power", "W"),
64958
+ powerTarget("energy", "kWh"),
64959
+ powerTarget("voltage", "V"),
64960
+ powerTarget("current", "A")
64961
+ ]
64962
+ };
63970
64963
  var powerMeterCapability = {
63971
64964
  name: "power-meter",
63972
64965
  scope: "device",
63973
64966
  deviceNative: true,
63974
64967
  mode: "singleton",
63975
- deviceTypes: [DeviceType.Sensor],
64968
+ deviceTypes: POWER_METER_DEVICE_TYPES,
63976
64969
  methods: {},
63977
64970
  status: {
63978
64971
  schema: PowerMeterStatusSchema,
@@ -63980,41 +64973,41 @@ var powerMeterCapability = {
63980
64973
  },
63981
64974
  runtimeState: PowerMeterStatusSchema,
63982
64975
  /**
63983
- * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
64976
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
63984
64977
  *
63985
64978
  * See `RuntimeStateDurability`. Enforced by
63986
64979
  * `scripts/check-runtime-state-durability.ts`.
63987
64980
  */
63988
64981
  durability: "restored",
63989
64982
  /**
63990
- * History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
64983
+ * History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
63991
64984
  */
63992
64985
  history: { fields: {
63993
- watts: {
64986
+ power: {
63994
64987
  kind: "gauge",
63995
64988
  unit: "W",
63996
64989
  deadband: 1,
63997
64990
  minIntervalMs: 3e4
63998
64991
  },
63999
- kwhTotal: {
64992
+ energy: {
64000
64993
  kind: "counter",
64001
64994
  unit: "kWh",
64002
64995
  deadband: .01,
64003
64996
  minIntervalMs: 6e4
64004
64997
  },
64005
- volts: {
64998
+ voltage: {
64006
64999
  kind: "gauge",
64007
65000
  unit: "V",
64008
65001
  deadband: 1,
64009
65002
  minIntervalMs: 3e4
64010
65003
  },
64011
- amps: {
65004
+ current: {
64012
65005
  kind: "gauge",
64013
65006
  unit: "A",
64014
65007
  deadband: .05,
64015
65008
  minIntervalMs: 3e4
64016
65009
  },
64017
- va: {
65010
+ apparentPower: {
64018
65011
  kind: "gauge",
64019
65012
  unit: "VA",
64020
65013
  deadband: 1,
@@ -64029,7 +65022,8 @@ var powerMeterCapability = {
64029
65022
  } },
64030
65023
  /** Clock fields: written, but excluded from the compare that decides
64031
65024
  * whether persisting is worth a SQLite commit. */
64032
- volatileStateFields: ["lastFetchedAt"]
65025
+ volatileStateFields: ["lastFetchedAt"],
65026
+ customize: powerMeterCustomize
64033
65027
  };
64034
65028
  /**
64035
65029
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -64063,9 +65057,6 @@ var PresenceStatusSchema = object({
64063
65057
  location: string().nullable(),
64064
65058
  /** GPS coordinates when available. Null otherwise. */
64065
65059
  gps: GpsLocationSchema.nullable(),
64066
- /** Optional battery level of the tracking device (0..100). Null
64067
- * when not reported. */
64068
- batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64069
65060
  /** Ms epoch when the slice was last updated. */
64070
65061
  lastChangedAt: number()
64071
65062
  });
@@ -64094,14 +65085,7 @@ var presenceCapability = {
64094
65085
  * `scripts/check-runtime-state-durability.ts`.
64095
65086
  */
64096
65087
  durability: "restored",
64097
- /**
64098
- * History (D850): recorded series.
64099
- */
64100
- history: { fields: { batteryPercent: {
64101
- kind: "gauge",
64102
- unit: "%",
64103
- deadband: 1
64104
- } } },
65088
+ history: "none",
64105
65089
  /** Clock fields: written, but excluded from the compare that decides
64106
65090
  * whether persisting is worth a SQLite commit. */
64107
65091
  volatileStateFields: ["lastChangedAt"]
@@ -64116,7 +65100,7 @@ var presenceCapability = {
64116
65100
  */
64117
65101
  var PressureSensorStatusSchema = object({
64118
65102
  /** Current pressure in hPa. `null` when the sample was refused (D393). */
64119
- hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
65103
+ pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
64120
65104
  /** Ms epoch when the slice was last updated. */
64121
65105
  lastFetchedAt: number()
64122
65106
  });
@@ -64142,7 +65126,7 @@ var pressureSensorCapability = {
64142
65126
  /**
64143
65127
  * History (D850): recorded series.
64144
65128
  */
64145
- history: { fields: { hpa: {
65129
+ history: { fields: { pressure: {
64146
65130
  kind: "gauge",
64147
65131
  unit: "hPa",
64148
65132
  deadband: .1
@@ -64710,6 +65694,8 @@ var runningCustomize = {
64710
65694
  stateField: "running"
64711
65695
  },
64712
65696
  deviceTypes: [
65697
+ DeviceType.Washer,
65698
+ DeviceType.Dryer,
64713
65699
  DeviceType.Dishwasher,
64714
65700
  DeviceType.Vacuum,
64715
65701
  DeviceType.LawnMower,
@@ -67199,7 +68185,8 @@ var switchCapability = {
67199
68185
  deviceTypes: [
67200
68186
  DeviceType.Switch,
67201
68187
  DeviceType.Siren,
67202
- DeviceType.Light
68188
+ DeviceType.Light,
68189
+ DeviceType.Fan
67203
68190
  ],
67204
68191
  methods: { setState: method(object({
67205
68192
  deviceId: number(),
@@ -67838,7 +68825,7 @@ var tamperCapability = {
67838
68825
  */
67839
68826
  var TemperatureSensorStatusSchema = object({
67840
68827
  /** Current temperature in Celsius. `null` when the sample was refused (D393). */
67841
- celsius: number().nullable().meta(readingMeta("temperature", {
68828
+ temperature: number().nullable().meta(readingMeta("temperature", {
67842
68829
  precision: 1,
67843
68830
  label: "Temperature"
67844
68831
  })),
@@ -67867,7 +68854,7 @@ var temperatureSensorCapability = {
67867
68854
  /**
67868
68855
  * History (D850): recorded series.
67869
68856
  */
67870
- history: { fields: { celsius: {
68857
+ history: { fields: { temperature: {
67871
68858
  kind: "gauge",
67872
68859
  unit: "°C",
67873
68860
  deadband: .1
@@ -68164,7 +69151,7 @@ var VacuumControlStatusSchema = object({
68164
69151
  /** Speed tokens the hardware accepts — drives the UI selector. */
68165
69152
  availableFanSpeeds: array(string()),
68166
69153
  /** 0..100 cleaning-completion percentage of the current task, or null. */
68167
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
69154
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
68168
69155
  /** Current error code (0 / null = no error). */
68169
69156
  errorCode: number().nullable().meta(readingMeta("none")),
68170
69157
  /** Human label for {@link errorCode}, or null when none / undecodable. */
@@ -69412,8 +70399,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
69412
70399
  kind: "mutation",
69413
70400
  auth: "admin"
69414
70401
  });
70402
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
69415
70403
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
69416
- 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;
70404
+ 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;
69417
70405
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
69418
70406
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
69419
70407
  new Set(CAP_NAMES);
@@ -69742,6 +70730,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
69742
70730
  featureProbe: featureProbeCapability,
69743
70731
  flood: floodCapability,
69744
70732
  gas: gasCapability,
70733
+ groupActive: groupActiveCapability,
69745
70734
  groupCommands: groupCommandsCapability,
69746
70735
  groupStatus: groupStatusCapability,
69747
70736
  heatingValve: heatingValveCapability,
@@ -69753,6 +70742,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
69753
70742
  lawnMowerControl: lawnMowerControlCapability,
69754
70743
  lockControl: lockControlCapability,
69755
70744
  mediaPlayer: mediaPlayerCapability,
70745
+ meter: meterCapability,
69756
70746
  motion: motionCapability,
69757
70747
  motionTrigger: motionTriggerCapability,
69758
70748
  motionZones: motionZonesCapability,
@@ -69815,7 +70805,7 @@ var BaseDevice = class {
69815
70805
  * `device-local-state.ts`. Drivers access via:
69816
70806
  *
69817
70807
  * `this.state.battery.sleeping = true` // patches the battery slice
69818
- * `const pct = this.state.battery.percentage` // reads the battery slice
70808
+ * `const pct = this.state.battery.level` // reads the battery slice
69819
70809
  * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
69820
70810
  *
69821
70811
  * Adding a new cap with `runtimeState:` automatically extends this
@@ -69855,7 +70845,7 @@ var BaseDevice = class {
69855
70845
  * shape; drivers don't redeclare it, they just write through.
69856
70846
  *
69857
70847
  * Read: `this.runtimeState.getCapState('battery')` →
69858
- * `{percentage, charging, sleeping, lastUpdated}` for any
70848
+ * `{level, charging, sleeping, lastUpdated}` for any
69859
70849
  * provider that registers `batteryCapability`.
69860
70850
  * Write: `this.runtimeState.setCapState('battery', { … })`.
69861
70851
  *
@@ -71012,7 +72002,7 @@ var BaseDeviceProvider = class extends BaseAddon {
71012
72002
  return toDeviceSummary(device, this.addonId);
71013
72003
  }
71014
72004
  };
71015
- 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;
72005
+ 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;
71016
72006
  new Set(Object.values(DeviceType));
71017
72007
  Object.freeze({
71018
72008
  "accessories.getStatus": {
@@ -71029,6 +72019,104 @@ Object.freeze({
71029
72019
  access: "create",
71030
72020
  role: "admin"
71031
72021
  },
72022
+ "actions.create": {
72023
+ capName: "actions",
72024
+ capScope: "system",
72025
+ addonId: null,
72026
+ access: "create",
72027
+ role: "admin"
72028
+ },
72029
+ "actions.delete": {
72030
+ capName: "actions",
72031
+ capScope: "system",
72032
+ addonId: null,
72033
+ access: "delete",
72034
+ role: "admin"
72035
+ },
72036
+ "actions.get": {
72037
+ capName: "actions",
72038
+ capScope: "system",
72039
+ addonId: null,
72040
+ access: "view",
72041
+ role: "viewer"
72042
+ },
72043
+ "actions.getLinks": {
72044
+ capName: "actions",
72045
+ capScope: "system",
72046
+ addonId: null,
72047
+ access: "view",
72048
+ role: "admin"
72049
+ },
72050
+ "actions.getTraces": {
72051
+ capName: "actions",
72052
+ capScope: "system",
72053
+ addonId: null,
72054
+ access: "view",
72055
+ role: "admin"
72056
+ },
72057
+ "actions.list": {
72058
+ capName: "actions",
72059
+ capScope: "system",
72060
+ addonId: null,
72061
+ access: "view",
72062
+ role: "admin"
72063
+ },
72064
+ "actions.listForCamera": {
72065
+ capName: "actions",
72066
+ capScope: "system",
72067
+ addonId: null,
72068
+ access: "view",
72069
+ role: "viewer"
72070
+ },
72071
+ "actions.run": {
72072
+ capName: "actions",
72073
+ capScope: "system",
72074
+ addonId: null,
72075
+ access: "create",
72076
+ role: "viewer"
72077
+ },
72078
+ "actions.setLinks": {
72079
+ capName: "actions",
72080
+ capScope: "system",
72081
+ addonId: null,
72082
+ access: "create",
72083
+ role: "admin"
72084
+ },
72085
+ "actions.stop": {
72086
+ capName: "actions",
72087
+ capScope: "system",
72088
+ addonId: null,
72089
+ access: "create",
72090
+ role: "admin"
72091
+ },
72092
+ "actions.update": {
72093
+ capName: "actions",
72094
+ capScope: "system",
72095
+ addonId: null,
72096
+ access: "create",
72097
+ role: "admin"
72098
+ },
72099
+ "actionSource.listActions": {
72100
+ capName: "action-source",
72101
+ capScope: "system",
72102
+ addonId: null,
72103
+ access: "view",
72104
+ role: "admin"
72105
+ },
72106
+ "actionSource.runAction": {
72107
+ capName: "action-source",
72108
+ capScope: "system",
72109
+ addonId: null,
72110
+ access: "create",
72111
+ role: "admin"
72112
+ },
72113
+ "actionSource.stopAction": {
72114
+ capName: "action-source",
72115
+ capScope: "system",
72116
+ addonId: null,
72117
+ access: "create",
72118
+ role: "admin"
72119
+ },
71032
72120
  "addonPages.listPages": {
71033
72121
  capName: "addon-pages",
71034
72122
  capScope: "system",
@@ -72310,6 +73398,13 @@ Object.freeze({
72310
73398
  access: "view",
72311
73399
  role: "admin"
72312
73400
  },
73401
+ "coreBlocks.getAutomationRunState": {
73402
+ capName: "core-blocks",
73403
+ capScope: "system",
73404
+ addonId: null,
73405
+ access: "view",
73406
+ role: "admin"
73407
+ },
72313
73408
  "coreBlocks.getBlockSettings": {
72314
73409
  capName: "core-blocks",
72315
73410
  capScope: "system",
@@ -72331,6 +73426,13 @@ Object.freeze({
72331
73426
  access: "view",
72332
73427
  role: "admin"
72333
73428
  },
73429
+ "coreBlocks.getTraces": {
73430
+ capName: "core-blocks",
73431
+ capScope: "system",
73432
+ addonId: null,
73433
+ access: "view",
73434
+ role: "admin"
73435
+ },
72334
73436
  "coreBlocks.getTypeDefs": {
72335
73437
  capName: "core-blocks",
72336
73438
  capScope: "system",
@@ -72366,6 +73468,13 @@ Object.freeze({
72366
73468
  access: "create",
72367
73469
  role: "admin"
72368
73470
  },
73471
+ "coreBlocks.reportAutomationRun": {
73472
+ capName: "core-blocks",
73473
+ capScope: "system",
73474
+ addonId: null,
73475
+ access: "create",
73476
+ role: "admin"
73477
+ },
72369
73478
  "coreBlocks.restart": {
72370
73479
  capName: "core-blocks",
72371
73480
  capScope: "system",
@@ -74235,6 +75344,13 @@ Object.freeze({
74235
75344
  access: "view",
74236
75345
  role: "viewer"
74237
75346
  },
75347
+ "groupActive.getStatus": {
75348
+ capName: "group-active",
75349
+ capScope: "device",
75350
+ addonId: null,
75351
+ access: "view",
75352
+ role: "viewer"
75353
+ },
74238
75354
  "groupCommands.getStatus": {
74239
75355
  capName: "group-commands",
74240
75356
  capScope: "device",
@@ -75201,6 +76317,13 @@ Object.freeze({
75201
76317
  access: "create",
75202
76318
  role: "admin"
75203
76319
  },
76320
+ "meter.getStatus": {
76321
+ capName: "meter",
76322
+ capScope: "device",
76323
+ addonId: null,
76324
+ access: "view",
76325
+ role: "viewer"
76326
+ },
75204
76327
  "metricsProvider.collectSnapshot": {
75205
76328
  capName: "metrics-provider",
75206
76329
  capScope: "system",
@@ -80925,7 +82048,7 @@ var ACTUATOR_CAP_VOCABULARY = {
80925
82048
  label: t("Full brightness", "Luminosità massima"),
80926
82049
  phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
80927
82050
  icon: "sun",
80928
- args: { percentage: 100 }
82051
+ args: { level: 100 }
80929
82052
  },
80930
82053
  {
80931
82054
  id: "brightness.half",
@@ -80933,7 +82056,7 @@ var ACTUATOR_CAP_VOCABULARY = {
80933
82056
  label: t("Half brightness", "Luminosità a metà"),
80934
82057
  phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
80935
82058
  icon: "sun-dim",
80936
- args: { percentage: 50 }
82059
+ args: { level: 50 }
80937
82060
  },
80938
82061
  {
80939
82062
  id: "brightness.off",
@@ -80941,16 +82064,16 @@ var ACTUATOR_CAP_VOCABULARY = {
80941
82064
  label: t("Brightness off", "Luminosità a zero"),
80942
82065
  phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
80943
82066
  icon: "lightbulb-off",
80944
- args: { percentage: 0 }
82067
+ args: { level: 0 }
80945
82068
  },
80946
82069
  {
80947
82070
  id: "brightness.set",
80948
82071
  method: "setBrightness",
80949
82072
  label: t("Set brightness", "Imposta la luminosità"),
80950
- phrase: t("Set the brightness of {device} to {percentage}", "Imposta la luminosità di {device} a {percentage}"),
82073
+ phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
80951
82074
  icon: "sun-medium",
80952
82075
  openArgs: [{
80953
- key: "percentage",
82076
+ key: "level",
80954
82077
  label: PERCENT,
80955
82078
  offer: {
80956
82079
  kind: "number",
@@ -80964,13 +82087,13 @@ var ACTUATOR_CAP_VOCABULARY = {
80964
82087
  ],
80965
82088
  state: [{
80966
82089
  kind: "numeric",
80967
- field: "percentage",
82090
+ field: "level",
80968
82091
  quantity: t("the brightness", "la luminosità"),
80969
82092
  label: t("Brightness", "Luminosità"),
80970
82093
  unit: "%",
80971
82094
  icon: "sun"
80972
82095
  }],
80973
- events: { onBrightnessChanged: { notOperatorFacing: "the `percentage` state trigger reads the same change" } }
82096
+ events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
80974
82097
  },
80975
82098
  color: {
80976
82099
  scope: "device",
@@ -80984,7 +82107,7 @@ var ACTUATOR_CAP_VOCABULARY = {
80984
82107
  icon: "sun",
80985
82108
  args: { color: {
80986
82109
  mode: "mired",
80987
- mireds: 370
82110
+ colorTemperature: 370
80988
82111
  } }
80989
82112
  },
80990
82113
  {
@@ -80995,7 +82118,7 @@ var ACTUATOR_CAP_VOCABULARY = {
80995
82118
  icon: "snowflake",
80996
82119
  args: { color: {
80997
82120
  mode: "mired",
80998
- mireds: 200
82121
+ colorTemperature: 200
80999
82122
  } }
81000
82123
  },
81001
82124
  {
@@ -81609,7 +82732,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81609
82732
  label: t("Fan off", "Ventilatore spento"),
81610
82733
  phrase: t("Stop {device}", "Ferma {device}"),
81611
82734
  icon: "power-off",
81612
- args: { percentage: 0 }
82735
+ args: { speed: 0 }
81613
82736
  },
81614
82737
  {
81615
82738
  id: "fan.full",
@@ -81617,16 +82740,16 @@ var ACTUATOR_CAP_VOCABULARY = {
81617
82740
  label: t("Fan full", "Ventilatore al massimo"),
81618
82741
  phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
81619
82742
  icon: "fan",
81620
- args: { percentage: 100 }
82743
+ args: { speed: 100 }
81621
82744
  },
81622
82745
  {
81623
82746
  id: "fan.speed",
81624
82747
  method: "setPercentage",
81625
82748
  label: t("Set speed", "Imposta la velocità"),
81626
- phrase: t("Set the speed of {device} to {percentage}", "Imposta la velocità di {device} al {percentage}"),
82749
+ phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
81627
82750
  icon: "fan",
81628
82751
  openArgs: [{
81629
- key: "percentage",
82752
+ key: "speed",
81630
82753
  label: t("Speed", "Velocità"),
81631
82754
  offer: {
81632
82755
  kind: "number",
@@ -81688,7 +82811,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81688
82811
  ],
81689
82812
  state: [{
81690
82813
  kind: "numeric",
81691
- field: "percentage",
82814
+ field: "speed",
81692
82815
  quantity: t("the speed", "la velocità"),
81693
82816
  label: t("Speed", "Velocità"),
81694
82817
  unit: "%",
@@ -82486,7 +83609,7 @@ var APPLIANCE_CAP_VOCABULARY = {
82486
83609
  },
82487
83610
  {
82488
83611
  kind: "numeric",
82489
- field: "progressPercent",
83612
+ field: "progress",
82490
83613
  quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
82491
83614
  label: t("Progress", "Avanzamento"),
82492
83615
  unit: "%",
@@ -82844,10 +83967,10 @@ var APPLIANCE_CAP_VOCABULARY = {
82844
83967
  id: "media.seek",
82845
83968
  method: "seek",
82846
83969
  label: t("Seek", "Vai al punto"),
82847
- phrase: t("Move {device} to {positionMs}", "Porta {device} al punto {positionMs}"),
83970
+ phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
82848
83971
  icon: "fast-forward",
82849
83972
  openArgs: [{
82850
- key: "positionMs",
83973
+ key: "position",
82851
83974
  label: t("Position", "Posizione"),
82852
83975
  offer: {
82853
83976
  kind: "number",
@@ -83299,7 +84422,39 @@ var APPLIANCE_CAP_VOCABULARY = {
83299
84422
  phrase: t("When {device} finishes", "Quando {device} finisce"),
83300
84423
  icon: "square"
83301
84424
  }
83302
- }
84425
+ },
84426
+ overrides: { byType: {
84427
+ [DeviceType.Washer]: {
84428
+ state: { running: {
84429
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
84430
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
84431
+ } },
84432
+ events: {
84433
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
84434
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
84435
+ }
84436
+ },
84437
+ [DeviceType.Dryer]: {
84438
+ state: { running: {
84439
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
84440
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
84441
+ } },
84442
+ events: {
84443
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
84444
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
84445
+ }
84446
+ },
84447
+ [DeviceType.Dishwasher]: {
84448
+ state: { running: {
84449
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
84450
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
84451
+ } },
84452
+ events: {
84453
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
84454
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
84455
+ }
84456
+ }
84457
+ } }
83303
84458
  },
83304
84459
  update: {
83305
84460
  scope: "device",
@@ -83864,7 +85019,7 @@ var SENSOR_CAP_VOCABULARY = {
83864
85019
  state: [
83865
85020
  {
83866
85021
  kind: "numeric",
83867
- field: "co2Ppm",
85022
+ field: "co2",
83868
85023
  quantity: t("the CO₂", "la CO₂"),
83869
85024
  label: t("CO₂", "CO₂"),
83870
85025
  unit: "ppm",
@@ -83872,7 +85027,7 @@ var SENSOR_CAP_VOCABULARY = {
83872
85027
  },
83873
85028
  {
83874
85029
  kind: "numeric",
83875
- field: "vocPpb",
85030
+ field: "voc",
83876
85031
  quantity: t("the VOC level", "il livello di COV"),
83877
85032
  label: t("VOC", "COV"),
83878
85033
  unit: "ppb",
@@ -83908,7 +85063,7 @@ var SENSOR_CAP_VOCABULARY = {
83908
85063
  noun: t("Light level", "Luminosità ambiente"),
83909
85064
  state: [{
83910
85065
  kind: "numeric",
83911
- field: "lux",
85066
+ field: "illuminance",
83912
85067
  quantity: t("the light level", "la luminosità"),
83913
85068
  label: t("Illuminance", "Illuminamento"),
83914
85069
  unit: "lx",
@@ -84071,6 +85226,10 @@ var SENSOR_CAP_VOCABULARY = {
84071
85226
  icon: "flame"
84072
85227
  }]
84073
85228
  },
85229
+ "group-active": {
85230
+ scope: "device",
85231
+ noun: t("Group state", "Stato del gruppo")
85232
+ },
84074
85233
  "group-commands": {
84075
85234
  scope: "device",
84076
85235
  noun: t("Group command", "Comando di gruppo")
@@ -84107,7 +85266,7 @@ var SENSOR_CAP_VOCABULARY = {
84107
85266
  noun: t("Humidity", "Umidità"),
84108
85267
  state: [{
84109
85268
  kind: "numeric",
84110
- field: "percent",
85269
+ field: "humidity",
84111
85270
  quantity: t("the humidity", "l’umidità"),
84112
85271
  label: t("Humidity", "Umidità"),
84113
85272
  unit: "%",
@@ -84127,6 +85286,17 @@ var SENSOR_CAP_VOCABULARY = {
84127
85286
  }],
84128
85287
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
84129
85288
  },
85289
+ meter: {
85290
+ scope: "device",
85291
+ noun: t("Meter", "Contatore"),
85292
+ state: [{
85293
+ kind: "numeric",
85294
+ field: "value",
85295
+ quantity: t("the total", "il totale"),
85296
+ label: t("Total", "Totale"),
85297
+ icon: "gauge"
85298
+ }]
85299
+ },
84130
85300
  "numeric-sensor": {
84131
85301
  scope: "device",
84132
85302
  noun: t("Sensor", "Sensore"),
@@ -84144,7 +85314,7 @@ var SENSOR_CAP_VOCABULARY = {
84144
85314
  state: [
84145
85315
  {
84146
85316
  kind: "numeric",
84147
- field: "watts",
85317
+ field: "power",
84148
85318
  quantity: t("the power draw", "il consumo"),
84149
85319
  label: t("Power", "Potenza"),
84150
85320
  unit: "W",
@@ -84152,7 +85322,7 @@ var SENSOR_CAP_VOCABULARY = {
84152
85322
  },
84153
85323
  {
84154
85324
  kind: "numeric",
84155
- field: "kwhTotal",
85325
+ field: "energy",
84156
85326
  quantity: t("the energy used", "l’energia consumata"),
84157
85327
  label: t("Energy", "Energia"),
84158
85328
  unit: "kWh",
@@ -84160,7 +85330,7 @@ var SENSOR_CAP_VOCABULARY = {
84160
85330
  },
84161
85331
  {
84162
85332
  kind: "numeric",
84163
- field: "volts",
85333
+ field: "voltage",
84164
85334
  quantity: t("the voltage", "la tensione"),
84165
85335
  label: t("Voltage", "Tensione"),
84166
85336
  unit: "V",
@@ -84168,7 +85338,7 @@ var SENSOR_CAP_VOCABULARY = {
84168
85338
  },
84169
85339
  {
84170
85340
  kind: "numeric",
84171
- field: "amps",
85341
+ field: "current",
84172
85342
  quantity: t("the current", "la corrente"),
84173
85343
  label: t("Current", "Corrente"),
84174
85344
  unit: "A",
@@ -84185,13 +85355,6 @@ var SENSOR_CAP_VOCABULARY = {
84185
85355
  label: t("Where", "Posizione"),
84186
85356
  phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
84187
85357
  icon: "map-pin"
84188
- }, {
84189
- kind: "numeric",
84190
- field: "batteryPercent",
84191
- quantity: t("the phone battery", "la batteria del telefono"),
84192
- label: t("Battery", "Batteria"),
84193
- unit: "%",
84194
- icon: "battery"
84195
85358
  }]
84196
85359
  },
84197
85360
  "pressure-sensor": {
@@ -84199,7 +85362,7 @@ var SENSOR_CAP_VOCABULARY = {
84199
85362
  noun: t("Pressure", "Pressione"),
84200
85363
  state: [{
84201
85364
  kind: "numeric",
84202
- field: "hpa",
85365
+ field: "pressure",
84203
85366
  quantity: t("the pressure", "la pressione"),
84204
85367
  label: t("Pressure", "Pressione"),
84205
85368
  unit: "hPa",
@@ -84235,7 +85398,7 @@ var SENSOR_CAP_VOCABULARY = {
84235
85398
  noun: t("Temperature", "Temperatura"),
84236
85399
  state: [{
84237
85400
  kind: "numeric",
84238
- field: "celsius",
85401
+ field: "temperature",
84239
85402
  quantity: t("the temperature", "la temperatura"),
84240
85403
  label: t("Temperature", "Temperatura"),
84241
85404
  unit: "°C",
@@ -84306,7 +85469,7 @@ var SENSOR_CAP_VOCABULARY = {
84306
85469
  state: [
84307
85470
  {
84308
85471
  kind: "numeric",
84309
- field: "percentage",
85472
+ field: "level",
84310
85473
  quantity: t("the battery level", "il livello della batteria"),
84311
85474
  label: t("Battery", "Batteria"),
84312
85475
  unit: "%",
@@ -84348,7 +85511,7 @@ var SENSOR_CAP_VOCABULARY = {
84348
85511
  icon: "battery-charging"
84349
85512
  }
84350
85513
  ],
84351
- events: { onStatusChanged: { notOperatorFacing: "the percentage / sleeping / charging state triggers each read one field of this change" } },
85514
+ events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
84352
85515
  extraEvents: [{
84353
85516
  category: EventCategory.DeviceAwake,
84354
85517
  label: t("Woke up", "Risvegliato"),
@@ -84366,7 +85529,7 @@ var SENSOR_CAP_VOCABULARY = {
84366
85529
  noun: t("Network link", "Collegamento di rete"),
84367
85530
  state: [{
84368
85531
  kind: "numeric",
84369
- field: "signalPercent",
85532
+ field: "signal",
84370
85533
  quantity: t("the signal strength", "l’intensità del segnale"),
84371
85534
  label: t("Signal", "Segnale"),
84372
85535
  unit: "%",
@@ -84459,6 +85622,8 @@ var ML = "model and training machinery — no device action";
84459
85622
  var INTERNAL = "internal plumbing between addons — never operator-facing";
84460
85623
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
84461
85624
  var SYSTEM_CAP_VOCABULARY = {
85625
+ "action-source": system(INTERNAL),
85626
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
84462
85627
  "addon-pages": system(UI_PLUMBING),
84463
85628
  "addon-pages-source": system(UI_PLUMBING),
84464
85629
  "addon-routes": system(UI_PLUMBING),
@@ -84599,8 +85764,47 @@ function projectAction(cap, action) {
84599
85764
  };
84600
85765
  }
84601
85766
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
84602
- 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;
85767
+ 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;
84603
85768
  DeviceFeature.BatteryOperated;
85769
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
85770
+ 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.";
85771
+ 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.";
85772
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
85773
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
85774
+ 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.";
85775
+ function withheld(reason) {
85776
+ return {
85777
+ [DeviceType.Washer]: {
85778
+ exported: false,
85779
+ reason
85780
+ },
85781
+ [DeviceType.Dryer]: {
85782
+ exported: false,
85783
+ reason
85784
+ },
85785
+ [DeviceType.Dishwasher]: {
85786
+ exported: false,
85787
+ reason
85788
+ }
85789
+ };
85790
+ }
85791
+ function offered(received) {
85792
+ return {
85793
+ [DeviceType.Washer]: {
85794
+ exported: true,
85795
+ as: received
85796
+ },
85797
+ [DeviceType.Dryer]: {
85798
+ exported: true,
85799
+ as: received
85800
+ },
85801
+ [DeviceType.Dishwasher]: {
85802
+ exported: true,
85803
+ as: received
85804
+ }
85805
+ };
85806
+ }
85807
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
84604
85808
  Object.freeze({
84605
85809
  "accessories.getStatus": [{
84606
85810
  name: "deviceId",
@@ -84616,6 +85820,31 @@ Object.freeze({
84616
85820
  form: "single",
84617
85821
  optional: false
84618
85822
  }],
85823
+ "actions.get": [{
85824
+ name: "deviceId",
85825
+ form: "single",
85826
+ optional: true
85827
+ }],
85828
+ "actions.getLinks": [{
85829
+ name: "deviceId",
85830
+ form: "single",
85831
+ optional: false
85832
+ }],
85833
+ "actions.listForCamera": [{
85834
+ name: "deviceId",
85835
+ form: "single",
85836
+ optional: false
85837
+ }],
85838
+ "actions.run": [{
85839
+ name: "deviceId",
85840
+ form: "single",
85841
+ optional: true
85842
+ }],
85843
+ "actions.setLinks": [{
85844
+ name: "deviceId",
85845
+ form: "single",
85846
+ optional: false
85847
+ }],
84619
85848
  "addonSettings.getDeviceSettings": [{
84620
85849
  name: "deviceId",
84621
85850
  form: "single",
@@ -85567,6 +86796,11 @@ Object.freeze({
85567
86796
  form: "single",
85568
86797
  optional: false
85569
86798
  }],
86799
+ "groupActive.getStatus": [{
86800
+ name: "deviceId",
86801
+ form: "single",
86802
+ optional: false
86803
+ }],
85570
86804
  "groupCommands.getStatus": [{
85571
86805
  name: "deviceId",
85572
86806
  form: "single",
@@ -85777,6 +87011,11 @@ Object.freeze({
85777
87011
  form: "single",
85778
87012
  optional: false
85779
87013
  }],
87014
+ "meter.getStatus": [{
87015
+ name: "deviceId",
87016
+ form: "single",
87017
+ optional: false
87018
+ }],
85780
87019
  "motion.getStatus": [{
85781
87020
  name: "deviceId",
85782
87021
  form: "single",
@@ -87384,7 +88623,6 @@ Object.freeze({
87384
88623
  "network-access": "ingress",
87385
88624
  "smtp-provider": "email"
87386
88625
  });
87387
- DeviceType.Camera;
87388
88626
  /**
87389
88627
  * A saved Customize assignment (D936). The schema is the type: callers use
87390
88628
  * `z.infer`, they do not restate the shape.
@@ -88688,6 +89926,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
88688
89926
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
88689
89927
  DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
88690
89928
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
89929
+ String.raw`(?<=^|["'(\s>,\[{])`;
88691
89930
  var MB = 1024 * 1024;
88692
89931
  1024 * MB, 3072 * MB;
88693
89932
  //#endregion
@@ -89451,7 +90690,6 @@ var COLD_PRESENCE = {
89451
90690
  state: "not_home",
89452
90691
  location: null,
89453
90692
  gps: null,
89454
- batteryPercent: null,
89455
90693
  lastChangedAt: 0
89456
90694
  };
89457
90695
  /**
@@ -89582,7 +90820,7 @@ var UnifiFeatureDevice = class extends BaseDevice {
89582
90820
  switch (capForMetric(this.metric)) {
89583
90821
  case "temperature-sensor":
89584
90822
  this.runtimeState.setCapState("temperature-sensor", {
89585
- celsius: infra.cpuTempC ?? 0,
90823
+ temperature: infra.cpuTempC ?? 0,
89586
90824
  lastFetchedAt: now
89587
90825
  });
89588
90826
  return;
@@ -89619,7 +90857,6 @@ var UnifiFeatureDevice = class extends BaseDevice {
89619
90857
  state: client.connected ? "home" : "not_home",
89620
90858
  location: client.ip,
89621
90859
  gps: null,
89622
- batteryPercent: null,
89623
90860
  lastChangedAt: Date.now()
89624
90861
  };
89625
90862
  this.runtimeState.setCapState("presence", status);
@@ -89659,7 +90896,7 @@ var UnifiFeatureDevice = class extends BaseDevice {
89659
90896
  switch (capForMetric(this.metric)) {
89660
90897
  case "temperature-sensor": {
89661
90898
  const COLD = {
89662
- celsius: 0,
90899
+ temperature: 0,
89663
90900
  lastFetchedAt: 0
89664
90901
  };
89665
90902
  this.ctx.registerNativeCap(temperatureSensorCapability, { getStatus: async () => this.runtimeState.getCapState("temperature-sensor") ?? COLD });