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