@camstack/addon-provider-reolink 1.2.211 → 1.2.213

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.js CHANGED
@@ -36,7 +36,7 @@ let node_os = require("node:os");
36
36
  node_os = require_chunk.__toESM(node_os, 1);
37
37
  let node_fs = require("node:fs");
38
38
  node_fs = require_chunk.__toESM(node_fs, 1);
39
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
39
+ //#region ../types/dist/event-category-DmZDPDf_.mjs
40
40
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
41
41
  EventCategory["SystemBoot"] = "system.boot";
42
42
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -663,7 +663,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
663
663
  * (a track was promoted to a stationary-registry entry) or departs (the
664
664
  * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
665
665
  * the dedicated "Stationary" section — never the live event feed. Payload:
666
- * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
666
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
667
667
  */
668
668
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
669
669
  /**
@@ -801,6 +801,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
801
801
  */
802
802
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
803
803
  /**
804
+ * An action run started or ended. Emitted by the `actions` surface.
805
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
806
+ */
807
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
808
+ /**
804
809
  * Progress update from a running model conversion job.
805
810
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
806
811
  * Emitted by `addon-model-studio` on the converting node.
@@ -5479,7 +5484,7 @@ var ZodIssueCode = {
5479
5484
  var ZodFirstPartyTypeKind;
5480
5485
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5481
5486
  //#endregion
5482
- //#region ../types/dist/sleep-bykKrTgu.mjs
5487
+ //#region ../types/dist/sleep-Crlmu_tO.mjs
5483
5488
  /**
5484
5489
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5485
5490
  * window to float samples (D455).
@@ -8637,6 +8642,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
8637
8642
  /** Every capability name, so addon code can check a name without the schema graph. */
8638
8643
  var CAP_NAMES = Object.freeze([
8639
8644
  "accessories",
8645
+ "action-source",
8646
+ "actions",
8640
8647
  "addon-pages",
8641
8648
  "addon-pages-source",
8642
8649
  "addon-routes",
@@ -8710,6 +8717,7 @@ var CAP_NAMES = Object.freeze([
8710
8717
  "filesystem-browse",
8711
8718
  "flood",
8712
8719
  "gas",
8720
+ "group-active",
8713
8721
  "group-commands",
8714
8722
  "group-status",
8715
8723
  "heating-valve",
@@ -8732,6 +8740,7 @@ var CAP_NAMES = Object.freeze([
8732
8740
  "login-method",
8733
8741
  "media-player",
8734
8742
  "mesh-network",
8743
+ "meter",
8735
8744
  "metrics-provider",
8736
8745
  "model-convert",
8737
8746
  "model-distributor",
@@ -8823,11 +8832,11 @@ var CAP_NAMES = Object.freeze([
8823
8832
  Object.freeze({
8824
8833
  "air-quality-sensor": [
8825
8834
  {
8826
- "path": "co2Ppm",
8835
+ "path": "co2",
8827
8836
  "valueType": "number"
8828
8837
  },
8829
8838
  {
8830
- "path": "vocPpb",
8839
+ "path": "voc",
8831
8840
  "valueType": "number"
8832
8841
  },
8833
8842
  {
@@ -8877,7 +8886,7 @@ Object.freeze({
8877
8886
  }
8878
8887
  ],
8879
8888
  "ambient-light-sensor": [{
8880
- "path": "lux",
8889
+ "path": "illuminance",
8881
8890
  "valueType": "number"
8882
8891
  }],
8883
8892
  "audio-metrics": [
@@ -8938,7 +8947,7 @@ Object.freeze({
8938
8947
  ],
8939
8948
  "battery": [
8940
8949
  {
8941
- "path": "percentage",
8950
+ "path": "level",
8942
8951
  "valueType": "number"
8943
8952
  },
8944
8953
  {
@@ -8960,7 +8969,7 @@ Object.freeze({
8960
8969
  "valueType": "number"
8961
8970
  },
8962
8971
  {
8963
- "path": "volts",
8972
+ "path": "voltage",
8964
8973
  "valueType": "number"
8965
8974
  },
8966
8975
  {
@@ -8981,7 +8990,7 @@ Object.freeze({
8981
8990
  "valueType": "boolean"
8982
8991
  }],
8983
8992
  "brightness": [{
8984
- "path": "percentage",
8993
+ "path": "level",
8985
8994
  "valueType": "number"
8986
8995
  }],
8987
8996
  "broker": [{
@@ -9182,7 +9191,7 @@ Object.freeze({
9182
9191
  "valueType": "number"
9183
9192
  },
9184
9193
  {
9185
- "path": "mireds",
9194
+ "path": "colorTemperature",
9186
9195
  "valueType": "number"
9187
9196
  }
9188
9197
  ],
@@ -9361,15 +9370,15 @@ Object.freeze({
9361
9370
  "valueType": "string"
9362
9371
  },
9363
9372
  {
9364
- "path": "remainingSeconds",
9373
+ "path": "remaining",
9365
9374
  "valueType": "number"
9366
9375
  },
9367
9376
  {
9368
- "path": "delayedStartSeconds",
9377
+ "path": "delayedStart",
9369
9378
  "valueType": "number"
9370
9379
  },
9371
9380
  {
9372
- "path": "progressPercent",
9381
+ "path": "progress",
9373
9382
  "valueType": "number"
9374
9383
  },
9375
9384
  {
@@ -9483,7 +9492,7 @@ Object.freeze({
9483
9492
  ],
9484
9493
  "fan-control": [
9485
9494
  {
9486
- "path": "percentage",
9495
+ "path": "speed",
9487
9496
  "valueType": "number"
9488
9497
  },
9489
9498
  {
@@ -9530,6 +9539,10 @@ Object.freeze({
9530
9539
  "path": "detected",
9531
9540
  "valueType": "boolean"
9532
9541
  }],
9542
+ "group-active": [{
9543
+ "path": "active",
9544
+ "valueType": "boolean"
9545
+ }],
9533
9546
  "group-status": [
9534
9547
  {
9535
9548
  "path": "included",
@@ -9579,7 +9592,7 @@ Object.freeze({
9579
9592
  }
9580
9593
  ],
9581
9594
  "humidity-sensor": [{
9582
- "path": "percent",
9595
+ "path": "humidity",
9583
9596
  "valueType": "number"
9584
9597
  }],
9585
9598
  "image": [{
@@ -9689,7 +9702,7 @@ Object.freeze({
9689
9702
  "valueType": "number"
9690
9703
  },
9691
9704
  {
9692
- "path": "progressPercent",
9705
+ "path": "progress",
9693
9706
  "valueType": "number"
9694
9707
  },
9695
9708
  {
@@ -9768,11 +9781,11 @@ Object.freeze({
9768
9781
  "valueType": "string"
9769
9782
  },
9770
9783
  {
9771
- "path": "positionMs",
9784
+ "path": "position",
9772
9785
  "valueType": "number"
9773
9786
  },
9774
9787
  {
9775
- "path": "durationMs",
9788
+ "path": "duration",
9776
9789
  "valueType": "number"
9777
9790
  },
9778
9791
  {
@@ -9789,6 +9802,10 @@ Object.freeze({
9789
9802
  ]
9790
9803
  }
9791
9804
  ],
9805
+ "meter": [{
9806
+ "path": "value",
9807
+ "valueType": "number"
9808
+ }],
9792
9809
  "motion": [
9793
9810
  {
9794
9811
  "path": "detected",
@@ -9870,11 +9887,11 @@ Object.freeze({
9870
9887
  ]
9871
9888
  },
9872
9889
  {
9873
- "path": "signalPercent",
9890
+ "path": "signal",
9874
9891
  "valueType": "number"
9875
9892
  },
9876
9893
  {
9877
- "path": "rssiDbm",
9894
+ "path": "rssi",
9878
9895
  "valueType": "number"
9879
9896
  },
9880
9897
  {
@@ -9929,6 +9946,18 @@ Object.freeze({
9929
9946
  "path": "foodLevel",
9930
9947
  "valueType": "number"
9931
9948
  },
9949
+ {
9950
+ "path": "bowlFood",
9951
+ "valueType": "number"
9952
+ },
9953
+ {
9954
+ "path": "eating",
9955
+ "valueType": "boolean"
9956
+ },
9957
+ {
9958
+ "path": "supportsMarkRefilled",
9959
+ "valueType": "boolean"
9960
+ },
9932
9961
  {
9933
9962
  "path": "food1",
9934
9963
  "valueType": "number"
@@ -9993,23 +10022,23 @@ Object.freeze({
9993
10022
  ],
9994
10023
  "power-meter": [
9995
10024
  {
9996
- "path": "watts",
10025
+ "path": "power",
9997
10026
  "valueType": "number"
9998
10027
  },
9999
10028
  {
10000
- "path": "kwhTotal",
10029
+ "path": "energy",
10001
10030
  "valueType": "number"
10002
10031
  },
10003
10032
  {
10004
- "path": "volts",
10033
+ "path": "voltage",
10005
10034
  "valueType": "number"
10006
10035
  },
10007
10036
  {
10008
- "path": "amps",
10037
+ "path": "current",
10009
10038
  "valueType": "number"
10010
10039
  },
10011
10040
  {
10012
- "path": "va",
10041
+ "path": "apparentPower",
10013
10042
  "valueType": "number"
10014
10043
  },
10015
10044
  {
@@ -10037,14 +10066,10 @@ Object.freeze({
10037
10066
  {
10038
10067
  "path": "gps.accuracyMeters",
10039
10068
  "valueType": "number"
10040
- },
10041
- {
10042
- "path": "batteryPercent",
10043
- "valueType": "number"
10044
10069
  }
10045
10070
  ],
10046
10071
  "pressure-sensor": [{
10047
- "path": "hpa",
10072
+ "path": "pressure",
10048
10073
  "valueType": "number"
10049
10074
  }],
10050
10075
  "privacy-mask": [{
@@ -10358,7 +10383,7 @@ Object.freeze({
10358
10383
  "valueType": "boolean"
10359
10384
  }],
10360
10385
  "temperature-sensor": [{
10361
- "path": "celsius",
10386
+ "path": "temperature",
10362
10387
  "valueType": "number"
10363
10388
  }],
10364
10389
  "update": [
@@ -10406,7 +10431,7 @@ Object.freeze({
10406
10431
  "valueType": "string"
10407
10432
  },
10408
10433
  {
10409
- "path": "progressPercent",
10434
+ "path": "progress",
10410
10435
  "valueType": "number"
10411
10436
  },
10412
10437
  {
@@ -10570,7 +10595,7 @@ function sleep$2(ms) {
10570
10595
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
10571
10596
  }
10572
10597
  //#endregion
10573
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10598
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10574
10599
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10575
10600
  DeviceType["Camera"] = "camera";
10576
10601
  DeviceType["Hub"] = "hub";
@@ -10664,8 +10689,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10664
10689
  DeviceType["PetFeeder"] = "pet-feeder";
10665
10690
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10666
10691
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10667
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10692
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10693
+ * A plug-built dishwasher carries running-state and power instead. */
10668
10694
  DeviceType["Dishwasher"] = "dishwasher";
10695
+ /** Washing machine. A plug-built one is running while its power stays
10696
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10697
+ DeviceType["Washer"] = "washer";
10698
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10699
+ DeviceType["Dryer"] = "dryer";
10669
10700
  return DeviceType;
10670
10701
  }({});
10671
10702
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -10913,7 +10944,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
10913
10944
  return DeviceRole;
10914
10945
  }({});
10915
10946
  //#endregion
10916
- //#region ../types/dist/composition-OhpuespA.mjs
10947
+ //#region ../types/dist/composition-CZImLpOI.mjs
10917
10948
  /**
10918
10949
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10919
10950
  *
@@ -11228,6 +11259,13 @@ var table$1 = {
11228
11259
  apply: () => {
11229
11260
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11230
11261
  }
11262
+ },
11263
+ schmitt: {
11264
+ minArgs: 3,
11265
+ maxArgs: 5,
11266
+ apply: () => {
11267
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11268
+ }
11231
11269
  }
11232
11270
  };
11233
11271
  Object.freeze(Object.assign(Object.create(null), table$1));
@@ -11669,6 +11707,99 @@ function validateExpressionSource(src) {
11669
11707
  return null;
11670
11708
  }
11671
11709
  /**
11710
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11711
+ *
11712
+ * Standby of the control board is under 5 W; a motor or a heater is far
11713
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11714
+ * a cycle, so running starts only after 30 s above the line.
11715
+ *
11716
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11717
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11718
+ * report "finished" in the middle of the program, so the stop dwell is
11719
+ * 10 minutes on every type. The number is stored per type so one machine
11720
+ * can move later without moving the others.
11721
+ */
11722
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11723
+ var APPLIANCE_KINDS = [
11724
+ "washer",
11725
+ "dryer",
11726
+ "dishwasher"
11727
+ ];
11728
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11729
+ /**
11730
+ * Statistics and integrals over a history read (D944).
11731
+ *
11732
+ * Pure: the same points always give the same answer, so a restart recomputes
11733
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11734
+ * never the mean of the samples. A hole with no held value is not integrated
11735
+ * as zero. A truncated read is not published.
11736
+ */
11737
+ var STATISTIC_STATS = [
11738
+ "min",
11739
+ "max",
11740
+ "mean",
11741
+ "sum",
11742
+ "count",
11743
+ "change"
11744
+ ];
11745
+ /**
11746
+ * The range a statistics or integral helper asks history for (D944).
11747
+ *
11748
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11749
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11750
+ * is strictly longer than the longest that range can be. Forever qualifies.
11751
+ * Equal to a tier's retention is a refusal, not a short answer.
11752
+ */
11753
+ var MINUTE_MS = 6e4;
11754
+ var HOUR_MS = 60 * MINUTE_MS;
11755
+ var DAY_MS = 24 * HOUR_MS;
11756
+ var ROLLING_SPANS = [
11757
+ "1h",
11758
+ "24h",
11759
+ "2d",
11760
+ "7d",
11761
+ "30d",
11762
+ "custom"
11763
+ ];
11764
+ var CALENDAR_PERIODS = [
11765
+ "today",
11766
+ "yesterday",
11767
+ "this-week",
11768
+ "this-month"
11769
+ ];
11770
+ var RESET_PERIODS = [
11771
+ "never",
11772
+ "daily",
11773
+ "weekly",
11774
+ "monthly"
11775
+ ];
11776
+ 5 * MINUTE_MS;
11777
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
11778
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
11779
+ var rollingWindowObject = object({
11780
+ kind: literal("rolling"),
11781
+ span: _enum(ROLLING_SPANS),
11782
+ customMinutes: number().int().min(5).optional()
11783
+ }).check((payload) => {
11784
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
11785
+ code: "custom",
11786
+ message: "a custom range needs a duration in minutes",
11787
+ path: ["customMinutes"],
11788
+ input: payload.value
11789
+ });
11790
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
11791
+ code: "custom",
11792
+ message: "only a custom range carries a duration",
11793
+ path: ["customMinutes"],
11794
+ input: payload.value
11795
+ });
11796
+ });
11797
+ var RollingWindowSchema = rollingWindowObject;
11798
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
11799
+ kind: literal("calendar"),
11800
+ period: _enum(CALENDAR_PERIODS)
11801
+ })]);
11802
+ /**
11672
11803
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11673
11804
  * id. Split out so helper schemas can name it without a cycle through
11674
11805
  * `composition.ts` (D659).
@@ -11694,7 +11825,13 @@ var GROUP_TEMPLATE_IDS = [
11694
11825
  "occupancy",
11695
11826
  "binary",
11696
11827
  "switch",
11697
- "light"
11828
+ "light",
11829
+ "fan",
11830
+ "cover",
11831
+ "valve",
11832
+ "lock",
11833
+ "media-player",
11834
+ "climate"
11698
11835
  ];
11699
11836
  var NUMERIC_AGGREGATES = [
11700
11837
  "mean",
@@ -11724,14 +11861,73 @@ var HelperMemberSchema = object({
11724
11861
  cap: string().min(1),
11725
11862
  fieldPath: string().min(1)
11726
11863
  });
11727
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11728
- kind: literal("group"),
11729
- templateId: GroupTemplateIdSchema,
11730
- aggregate: AggregateReducerSchema,
11731
- members: array(HelperMemberSchema).min(1).max(64),
11732
- /** Actuator groups only. Readings leave this unset. */
11733
- active: ActiveCriterionSchema.optional()
11734
- })]);
11864
+ var HelperSpecSchema = discriminatedUnion("kind", [
11865
+ object({
11866
+ kind: literal("group"),
11867
+ templateId: GroupTemplateIdSchema,
11868
+ aggregate: AggregateReducerSchema,
11869
+ members: array(HelperMemberSchema).min(1).max(64),
11870
+ /** Actuator groups only. Readings leave this unset. */
11871
+ active: ActiveCriterionSchema.optional()
11872
+ }),
11873
+ object({
11874
+ kind: literal("appliance"),
11875
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
11876
+ appliance: _enum(APPLIANCE_KINDS),
11877
+ thresholdWatts: number().positive(),
11878
+ startDwellMs: number().int().nonnegative(),
11879
+ stopDwellMs: number().int().nonnegative(),
11880
+ member: HelperMemberSchema
11881
+ }),
11882
+ object({
11883
+ kind: literal("statistic"),
11884
+ stat: _enum(STATISTIC_STATS),
11885
+ window: StatisticWindowSchema,
11886
+ source: HelperMemberSchema
11887
+ }),
11888
+ object({
11889
+ kind: literal("integral"),
11890
+ reset: _enum(RESET_PERIODS),
11891
+ output: _enum(["energy", "meter"]),
11892
+ source: HelperMemberSchema
11893
+ }),
11894
+ object({
11895
+ kind: literal("threshold"),
11896
+ source: HelperMemberSchema,
11897
+ high: number(),
11898
+ low: number(),
11899
+ onForMs: number().int().nonnegative(),
11900
+ offForMs: number().int().nonnegative()
11901
+ }),
11902
+ object({
11903
+ kind: literal("arithmetic"),
11904
+ op: _enum([
11905
+ "sum",
11906
+ "difference",
11907
+ "ratio"
11908
+ ]),
11909
+ inputs: array(HelperMemberSchema).min(2).max(64)
11910
+ }),
11911
+ object({
11912
+ kind: literal("expression"),
11913
+ expr: string().min(1),
11914
+ inputs: array(object({
11915
+ name: string().min(1),
11916
+ member: HelperMemberSchema
11917
+ })).min(1).max(64),
11918
+ output: _enum(["number", "boolean"])
11919
+ }),
11920
+ object({
11921
+ kind: literal("derivative"),
11922
+ window: RollingWindowSchema,
11923
+ per: _enum([
11924
+ "second",
11925
+ "minute",
11926
+ "hour"
11927
+ ]),
11928
+ source: HelperMemberSchema
11929
+ })
11930
+ ]);
11735
11931
  /** A composed device's stableId is this prefix plus the block id. */
11736
11932
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11737
11933
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -11792,6 +11988,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11792
11988
  "all"
11793
11989
  ]),
11794
11990
  reads: array(CompositionFieldReadSchema).min(1).max(64),
11991
+ /**
11992
+ * Set when the reads are an enum state, not a yes/no: the listed states
11993
+ * count as active (a cover is `open`). A state outside the list is inactive;
11994
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
11995
+ */
11996
+ activeWhen: array(string().min(1)).min(1).optional(),
11795
11997
  /** `value` is the reading; the others are the member counts. */
11796
11998
  publish: _enum([
11797
11999
  "value",
@@ -11805,6 +12007,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11805
12007
  "omit",
11806
12008
  "offline"
11807
12009
  ])
12010
+ }),
12011
+ object({
12012
+ kind: literal("series"),
12013
+ read: CompositionFieldReadSchema,
12014
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12015
+ publish: _enum([
12016
+ "value",
12017
+ "included",
12018
+ "excluded",
12019
+ "total"
12020
+ ]),
12021
+ /**
12022
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12023
+ * device unseeded until history answers, so a restart cannot publish 0.
12024
+ */
12025
+ empty: _enum([
12026
+ "null",
12027
+ "omit",
12028
+ "offline"
12029
+ ])
11808
12030
  })
11809
12031
  ]);
11810
12032
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -15812,6 +16034,14 @@ var AutomationConditionLeafSchema = object({
15812
16034
  array(union([string(), number()]))
15813
16035
  ])
15814
16036
  });
16037
+ var AutomationConditionStationaryPresentSchema = object({
16038
+ kind: literal("stationary-present"),
16039
+ deviceId: number().int().nonnegative(),
16040
+ /** Class names; a macro class matches every raw class that maps to it. */
16041
+ classes: array(string().min(1)).min(1).optional(),
16042
+ /** The parked object must be in at least one of these zones. */
16043
+ zones: array(string().min(1)).min(1).optional()
16044
+ });
15815
16045
  /**
15816
16046
  * The expression leaf, declared as a plain object rather than an intersection
15817
16047
  * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
@@ -15845,7 +16075,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
15845
16075
  child: AutomationConditionSchema
15846
16076
  }),
15847
16077
  AutomationConditionLeafSchema,
15848
- AutomationConditionExpressionSchema
16078
+ AutomationConditionExpressionSchema,
16079
+ AutomationConditionStationaryPresentSchema
15849
16080
  ]));
15850
16081
  /**
15851
16082
  * What starts a run.
@@ -15886,7 +16117,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15886
16117
  kind: literal("schedule"),
15887
16118
  cron: string().min(1).max(120)
15888
16119
  }),
15889
- object({ kind: literal("manual") })
16120
+ object({ kind: literal("manual") }),
16121
+ object({
16122
+ kind: literal("detection"),
16123
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
16124
+ * matches every raw class that maps to it; any other name matches
16125
+ * exactly. */
16126
+ classes: array(string().min(1)).min(1),
16127
+ deviceIds: array(number().int().nonnegative()).min(1),
16128
+ /** The track must have visited at least one of these zones. */
16129
+ zones: array(string().min(1)).min(1).optional(),
16130
+ /** Best-so-far confidence the track must reach. */
16131
+ minScore: number().min(0).max(1).optional()
16132
+ }),
16133
+ object({
16134
+ kind: literal("stationary"),
16135
+ deviceIds: array(number().int().nonnegative()).min(1),
16136
+ /** Class names; a macro class matches like the detection trigger. */
16137
+ classes: array(string().min(1)).min(1).optional(),
16138
+ /** The parked object must be in at least one of these zones. */
16139
+ zones: array(string().min(1)).min(1).optional(),
16140
+ /** `appeared`: it became parked. `departed`: the registry retired it.
16141
+ * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
16142
+ phase: _enum(["appeared", "departed"])
16143
+ }),
16144
+ object({
16145
+ kind: literal("interval"),
16146
+ minutes: number().int().min(1).max(1440)
16147
+ })
15890
16148
  ]);
15891
16149
  /**
15892
16150
  * One action step.
@@ -15906,28 +16164,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15906
16164
  * is called. The runner still receives a number and is untouched; the
15907
16165
  * resolution is the recipe's job, not the runner's.
15908
16166
  */
16167
+ var AutomationWaitStepSchema = object({
16168
+ kind: literal("wait"),
16169
+ seconds: number().min(0).max(300)
16170
+ });
16171
+ var AutomationCapStepSchema = object({
16172
+ kind: literal("cap"),
16173
+ /**
16174
+ * One device, several same-family devices (locks / climate family / …),
16175
+ * or `{ $var }` for the device that triggered the run. An array is
16176
+ * fan-out at codegen — each id gets the same cap.method call.
16177
+ */
16178
+ deviceId: union([
16179
+ number().int(),
16180
+ array(number().int()).min(1),
16181
+ object({ $var: string().min(1) })
16182
+ ]),
16183
+ cap: string().min(1),
16184
+ method: string().min(1),
16185
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
16186
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
16187
+ * the expression leaf. */
16188
+ args: record(string(), unknown()).optional()
16189
+ });
16190
+ /**
16191
+ * The steps an internal Action may contain: cap and wait, and nothing that can
16192
+ * call back into an Action — so an Action can never reach itself (no cycles).
16193
+ */
16194
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
15909
16195
  var AutomationActionSchema = discriminatedUnion("kind", [
16196
+ AutomationWaitStepSchema,
16197
+ AutomationCapStepSchema,
15910
16198
  object({
15911
- kind: literal("wait"),
15912
- seconds: number().min(0).max(300)
15913
- }),
15914
- object({
15915
- kind: literal("cap"),
15916
- /**
15917
- * One device, several same-family devices (locks / climate family / …),
15918
- * or `{ $var }` for the device that triggered the run. An array is
15919
- * fan-out at codegen — each id gets the same cap.method call.
15920
- */
15921
- deviceId: union([
15922
- number().int(),
15923
- array(number().int()).min(1),
15924
- object({ $var: string().min(1) })
15925
- ]),
15926
- cap: string().min(1),
15927
- method: string().min(1),
15928
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
15929
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
15930
- args: record(string(), unknown()).optional()
16199
+ kind: literal("action"),
16200
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
16201
+ actionId: string().min(1),
16202
+ /** Values for the action's declared variables; the others take their default. */
16203
+ variables: record(string(), union([
16204
+ string(),
16205
+ number(),
16206
+ boolean()
16207
+ ])).optional()
15931
16208
  }),
15932
16209
  object({
15933
16210
  kind: literal("code"),
@@ -15936,10 +16213,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15936
16213
  code: string().min(1).max(2e4)
15937
16214
  })
15938
16215
  ]);
16216
+ /**
16217
+ * How a trigger that arrives while the automation is busy (or cooling down) is
16218
+ * handled. One of the automation's execution modes, never a property of its
16219
+ * actions. Absent means `single`.
16220
+ *
16221
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
16222
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
16223
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
16224
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
16225
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
16226
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
16227
+ * wait, never the single/queued limits.
16228
+ */
16229
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
16230
+ object({ mode: literal("single") }),
16231
+ object({ mode: literal("restart") }),
16232
+ object({
16233
+ mode: literal("queued"),
16234
+ max: number().int().min(1).max(10)
16235
+ }),
16236
+ object({
16237
+ mode: literal("parallel"),
16238
+ max: number().int().min(1).max(10)
16239
+ }),
16240
+ object({
16241
+ mode: literal("cooldown"),
16242
+ minutes: number().int().min(1).max(1440)
16243
+ })
16244
+ ]);
15939
16245
  var AutomationRecipeSchema = object({
15940
16246
  triggers: array(AutomationTriggerSchema),
15941
16247
  conditions: AutomationConditionSchema.optional(),
15942
- actions: array(AutomationActionSchema)
16248
+ actions: array(AutomationActionSchema),
16249
+ runMode: AutomationRunModeSchema.optional(),
16250
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
16251
+ keepTraces: number().int().min(1).max(100).optional()
16252
+ });
16253
+ new Set([
16254
+ {
16255
+ id: "person",
16256
+ name: "Person"
16257
+ },
16258
+ {
16259
+ id: "vehicle",
16260
+ name: "Vehicle"
16261
+ },
16262
+ {
16263
+ id: "animal",
16264
+ name: "Animal"
16265
+ },
16266
+ {
16267
+ id: "package",
16268
+ name: "Package"
16269
+ }
16270
+ ].map((l) => l.id));
16271
+ var COCO_TO_MACRO = {
16272
+ mapping: {
16273
+ person: "person",
16274
+ bicycle: "vehicle",
16275
+ car: "vehicle",
16276
+ motorcycle: "vehicle",
16277
+ airplane: "vehicle",
16278
+ bus: "vehicle",
16279
+ train: "vehicle",
16280
+ truck: "vehicle",
16281
+ boat: "vehicle",
16282
+ bird: "animal",
16283
+ cat: "animal",
16284
+ dog: "animal",
16285
+ horse: "animal",
16286
+ sheep: "animal",
16287
+ cow: "animal",
16288
+ elephant: "animal",
16289
+ bear: "animal",
16290
+ zebra: "animal",
16291
+ giraffe: "animal",
16292
+ suitcase: "package",
16293
+ backpack: "package",
16294
+ handbag: "package"
16295
+ },
16296
+ preserveOriginal: false
16297
+ };
16298
+ new Set(Object.values(COCO_TO_MACRO.mapping));
16299
+ /**
16300
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
16301
+ * so the editor can paint every block green or red with its reason.
16302
+ *
16303
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
16304
+ * every condition node (containers and leaves, including the ones a
16305
+ * short-circuit never reached), the run-mode verdict and every action step —
16306
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
16307
+ * for an Action run). Skipped
16308
+ * evaluations are traced too, and identical consecutive skips collapse into a
16309
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
16310
+ *
16311
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
16312
+ * vocabulary so an Action run records the same step shape.
16313
+ */
16314
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
16315
+ var TraceValueSchema = union([
16316
+ string(),
16317
+ number(),
16318
+ boolean(),
16319
+ _null(),
16320
+ array(union([string(), number()]))
16321
+ ]);
16322
+ var AutomationRunOutcomeSchema = _enum([
16323
+ "ran",
16324
+ "skipped-trigger",
16325
+ "skipped-conditions",
16326
+ "skipped-cooldown",
16327
+ "skipped-overlap",
16328
+ "failed",
16329
+ "aborted"
16330
+ ]);
16331
+ /** `not-evaluated` = a short-circuit never reached the node. */
16332
+ var TraceNodeOutcomeSchema = _enum([
16333
+ "pass",
16334
+ "fail",
16335
+ "not-evaluated"
16336
+ ]);
16337
+ var AutomationTriggerTraceSchema = object({
16338
+ kind: string().min(1),
16339
+ outcome: _enum(["pass", "fail"]),
16340
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
16341
+ reason: string(),
16342
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
16343
+ vars: record(string(), TraceValueSchema)
16344
+ });
16345
+ var AutomationConditionTraceNodeSchema = object({
16346
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
16347
+ path: string().min(1),
16348
+ kind: _enum([
16349
+ "all",
16350
+ "any",
16351
+ "not",
16352
+ "condition",
16353
+ "expression",
16354
+ "stationary-present"
16355
+ ]),
16356
+ outcome: TraceNodeOutcomeSchema,
16357
+ reason: string(),
16358
+ deviceId: number().int().optional(),
16359
+ cap: string().optional(),
16360
+ fieldPath: string().optional(),
16361
+ operator: string().optional(),
16362
+ expected: TraceValueSchema.optional(),
16363
+ /** What the mirror held; `null` when it held nothing (unknown). */
16364
+ observed: TraceValueSchema.optional()
16365
+ });
16366
+ var AutomationRunVerdictSchema = object({
16367
+ mode: AutomationRunModeSchema,
16368
+ decision: _enum([
16369
+ "run",
16370
+ "queued",
16371
+ "restart",
16372
+ "drop",
16373
+ "cooldown",
16374
+ "bypass"
16375
+ ]),
16376
+ reason: string()
16377
+ });
16378
+ /** One executed (or not-run) step — shared by automations and Action runs. */
16379
+ var AutomationStepTraceSchema = object({
16380
+ /** `actions.<i>` */
16381
+ path: string().min(1),
16382
+ kind: string().min(1),
16383
+ cap: string().optional(),
16384
+ method: string().optional(),
16385
+ deviceId: number().int().optional(),
16386
+ startedAt: number().optional(),
16387
+ endedAt: number().optional(),
16388
+ outcome: _enum([
16389
+ "ok",
16390
+ "error",
16391
+ "not-run"
16392
+ ]),
16393
+ message: string().optional()
16394
+ });
16395
+ var AutomationRunTraceSchema = object({
16396
+ runId: string().min(1),
16397
+ startedAt: number(),
16398
+ endedAt: number(),
16399
+ trigger: AutomationTriggerTraceSchema,
16400
+ conditions: array(AutomationConditionTraceNodeSchema),
16401
+ verdict: AutomationRunVerdictSchema.optional(),
16402
+ steps: array(AutomationStepTraceSchema),
16403
+ outcome: AutomationRunOutcomeSchema,
16404
+ /** How many identical consecutive skips this trace stands for (>= 1). */
16405
+ repeat: number().int().min(1),
16406
+ /** When the latest of those repeats happened. */
16407
+ lastAt: number()
16408
+ });
16409
+ /**
16410
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
16411
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
16412
+ */
16413
+ var AutomationStateDocSchema = object({
16414
+ version: literal(1),
16415
+ lastRunStartedAt: number().nullable(),
16416
+ traces: array(AutomationRunTraceSchema)
16417
+ });
16418
+ /**
16419
+ * Actions — named, runnable things an operator (or an automation) can start,
16420
+ * declared as LIST ENTRIES, never as devices.
16421
+ *
16422
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
16423
+ * hub's `actions` record kind) or declared by an addon through the
16424
+ * `action-source` collection (a Home Assistant script, a scene …). The one
16425
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
16426
+ * catalog of its own (D3) — every `list` asks the providers live.
16427
+ *
16428
+ * This file is the data half: the entry schema, the variables an entry may
16429
+ * declare, and the pure resolution of the values a run was given. It holds no
16430
+ * runtime.
16431
+ */
16432
+ /** What a variable's value is. `select` takes one of `options`. */
16433
+ var ActionVariableTypeSchema = _enum([
16434
+ "string",
16435
+ "number",
16436
+ "boolean",
16437
+ "select"
16438
+ ]);
16439
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
16440
+ var ActionVariableValueSchema = union([
16441
+ string(),
16442
+ number(),
16443
+ boolean()
16444
+ ]);
16445
+ /**
16446
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
16447
+ * value) or `{{vars.name}}` (inside a string) in their args.
16448
+ */
16449
+ var ActionVariableSchema = object({
16450
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
16451
+ label: string().min(1).max(120).optional(),
16452
+ type: ActionVariableTypeSchema,
16453
+ required: boolean().optional(),
16454
+ default: ActionVariableValueSchema.optional(),
16455
+ /** The allowed values of a `select`. */
16456
+ options: array(string().min(1)).min(1).optional()
16457
+ }).superRefine((v, ctx) => {
16458
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
16459
+ code: "custom",
16460
+ message: "a select variable needs options",
16461
+ path: ["options"]
16462
+ });
16463
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
16464
+ code: "custom",
16465
+ message: variableValueError(v, v.default) ?? "invalid default",
16466
+ path: ["default"]
16467
+ });
16468
+ });
16469
+ /** Why `value` is not a legal value for the variable, or null when it is. */
16470
+ function variableValueError(variable, value) {
16471
+ switch (variable.type) {
16472
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
16473
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
16474
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
16475
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
16476
+ }
16477
+ }
16478
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
16479
+ function isInternalActionId(actionId) {
16480
+ return actionId.startsWith("internal:") && actionId.length > 9;
16481
+ }
16482
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
16483
+ function addonIdOfActionId(actionId) {
16484
+ if (isInternalActionId(actionId)) return null;
16485
+ const at = actionId.indexOf(":");
16486
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
16487
+ }
16488
+ var ActionStatusSchema = object({
16489
+ isRunning: boolean(),
16490
+ /** Ms epoch the latest run STARTED; null when it never ran. */
16491
+ lastRunAt: number().nullable(),
16492
+ /** The latest run's failure; null on success or when it never ran. */
16493
+ lastError: string().nullable()
16494
+ });
16495
+ var ActionEntrySchema = object({
16496
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
16497
+ id: string().min(3),
16498
+ name: string().min(1).max(120),
16499
+ /** The addon that declared it; `core-blocks` for an internal one. */
16500
+ addonId: string().min(1),
16501
+ source: ActionSourceKindSchema,
16502
+ /** An addon-declared entry is edited where it was declared. */
16503
+ readOnly: boolean(),
16504
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
16505
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
16506
+ variables: array(ActionVariableSchema).max(20).optional(),
16507
+ status: ActionStatusSchema
16508
+ }).superRefine((entry, ctx) => {
16509
+ if (entry.source === "internal") {
16510
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
16511
+ code: "custom",
16512
+ message: "an internal action id is internal:<uuid>",
16513
+ path: ["id"]
16514
+ });
16515
+ return;
16516
+ }
16517
+ if (entry.steps !== void 0) ctx.addIssue({
16518
+ code: "custom",
16519
+ message: "only an internal action carries steps",
16520
+ path: ["steps"]
16521
+ });
16522
+ if (!entry.readOnly) ctx.addIssue({
16523
+ code: "custom",
16524
+ message: "an addon action is read-only here",
16525
+ path: ["readOnly"]
16526
+ });
16527
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
16528
+ code: "custom",
16529
+ message: "an addon action id is <addonId>:<localId>",
16530
+ path: ["id"]
16531
+ });
16532
+ });
16533
+ /** What an operator writes for an internal action. */
16534
+ var ActionInputSchema = object({
16535
+ name: string().min(1).max(120),
16536
+ steps: array(ActionStepSchema).min(1).max(50),
16537
+ variables: array(ActionVariableSchema).max(20).optional()
16538
+ });
16539
+ /** Who started a run. Absent = the operator pressed it. */
16540
+ var ActionRunOriginSchema = object({
16541
+ /** The automation block whose step started it. */
16542
+ blockId: string().min(1).optional() });
16543
+ /** `started` = the caller did not ask to wait. */
16544
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
16545
+ var ActionRunResultSchema = object({
16546
+ runId: string().min(1),
16547
+ outcome: ActionRunOutcomeSchema,
16548
+ /** Why it did not run, or what failed. */
16549
+ message: string().optional()
16550
+ });
16551
+ _enum([
16552
+ "not-found",
16553
+ "read-only",
16554
+ "invalid-variables",
16555
+ "invalid-steps",
16556
+ "not-linked",
16557
+ "busy"
16558
+ ]);
16559
+ object({
16560
+ actionId: string(),
16561
+ runId: string(),
16562
+ isRunning: boolean(),
16563
+ /** Set when the run ended. */
16564
+ outcome: AutomationRunOutcomeSchema.optional(),
16565
+ lastRunAt: number().nullable(),
16566
+ lastError: string().nullable()
15943
16567
  });
15944
16568
  /**
15945
16569
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -17534,7 +18158,22 @@ var BrokerProviderInfoSchema = object({
17534
18158
  */
17535
18159
  interactiveConnectPath: string().min(1).optional(),
17536
18160
  /** Relative hub path to disconnect the linked account (same popup pattern). */
17537
- interactiveDisconnectPath: string().min(1).optional()
18161
+ interactiveDisconnectPath: string().min(1).optional(),
18162
+ /**
18163
+ * The kind signs in through a server-held interactive session instead of
18164
+ * (or before) typed credentials. `basePath` is a hub-relative route set
18165
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
18166
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
18167
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
18168
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
18169
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
18170
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
18171
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
18172
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
18173
+ * offers it as the default path; the fields the session fills are
18174
+ * marked `filledBySignIn` in the kind's settings schema.
18175
+ */
18176
+ signIn: object({ basePath: string().min(1) }).optional()
17538
18177
  }))
17539
18178
  });
17540
18179
  var ListInputSchema = object({
@@ -18611,7 +19250,7 @@ var CompositionEventPlanSchema = object({
18611
19250
  stateField: string(),
18612
19251
  fieldKey: string()
18613
19252
  });
18614
- /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
19253
+ /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
18615
19254
  var CompositionProblemSchema = object({
18616
19255
  code: _enum([
18617
19256
  "reserved-variant",
@@ -18639,7 +19278,8 @@ var CompositionProblemSchema = object({
18639
19278
  "native-capability-replace",
18640
19279
  "mixed-quantity",
18641
19280
  "helper-mismatch",
18642
- "member-constraint"
19281
+ "member-constraint",
19282
+ "retention"
18643
19283
  ]),
18644
19284
  path: string(),
18645
19285
  message: string()
@@ -18873,13 +19513,17 @@ Object.freeze({
18873
19513
  latchedAt: {
18874
19514
  kind: "fixed",
18875
19515
  result: ["number", "null"]
19516
+ },
19517
+ schmitt: {
19518
+ kind: "fixed",
19519
+ result: ["boolean", "null"]
18876
19520
  }
18877
19521
  });
18878
19522
  /**
18879
19523
  * One catalog row per base reading a group helper can build (D944 §3).
18880
19524
  * Data, not a form default written into the block.
18881
19525
  */
18882
- var NOW = {
19526
+ var NOW$2 = {
18883
19527
  fieldPath: "lastFetchedAt",
18884
19528
  expr: "now"
18885
19529
  };
@@ -18893,6 +19537,8 @@ function numeric(input) {
18893
19537
  family: "numeric",
18894
19538
  deviceType: DeviceType.Sensor,
18895
19539
  memberDeviceType: DeviceType.Sensor,
19540
+ memberDeviceTypes: [DeviceType.Sensor],
19541
+ activeWhen: void 0,
18896
19542
  commandCaps: [],
18897
19543
  baseUnit: baseUnitOf(input.quantity),
18898
19544
  aggregates: NUMERIC_AGGREGATES,
@@ -18908,6 +19554,8 @@ function binary(input) {
18908
19554
  family: "binary",
18909
19555
  deviceType: DeviceType.Sensor,
18910
19556
  memberDeviceType: DeviceType.Sensor,
19557
+ memberDeviceTypes: [DeviceType.Sensor],
19558
+ activeWhen: void 0,
18911
19559
  commandCaps: [],
18912
19560
  quantity: "none",
18913
19561
  baseUnit: void 0,
@@ -18921,73 +19569,73 @@ var GROUP_TEMPLATES = [
18921
19569
  summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18922
19570
  role: DeviceRole.TemperatureSensor,
18923
19571
  outputCap: "temperature-sensor",
18924
- outputField: "celsius",
19572
+ outputField: "temperature",
18925
19573
  quantity: "temperature",
18926
19574
  defaultAggregate: "mean",
18927
19575
  empty: "null",
18928
- companions: [NOW]
19576
+ companions: [NOW$2]
18929
19577
  }),
18930
19578
  numeric({
18931
19579
  id: "humidity",
18932
19580
  summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18933
19581
  role: DeviceRole.HumiditySensor,
18934
19582
  outputCap: "humidity-sensor",
18935
- outputField: "percent",
19583
+ outputField: "humidity",
18936
19584
  quantity: "humidity",
18937
19585
  defaultAggregate: "mean",
18938
19586
  empty: "null",
18939
- companions: [NOW]
19587
+ companions: [NOW$2]
18940
19588
  }),
18941
19589
  numeric({
18942
19590
  id: "pressure",
18943
19591
  summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18944
19592
  role: DeviceRole.PressureSensor,
18945
19593
  outputCap: "pressure-sensor",
18946
- outputField: "hpa",
19594
+ outputField: "pressure",
18947
19595
  quantity: "pressure",
18948
19596
  defaultAggregate: "mean",
18949
19597
  empty: "null",
18950
- companions: [NOW]
19598
+ companions: [NOW$2]
18951
19599
  }),
18952
19600
  numeric({
18953
19601
  id: "illuminance",
18954
19602
  summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18955
19603
  role: DeviceRole.AmbientLightSensor,
18956
19604
  outputCap: "ambient-light-sensor",
18957
- outputField: "lux",
19605
+ outputField: "illuminance",
18958
19606
  quantity: "illuminance",
18959
19607
  defaultAggregate: "mean",
18960
19608
  empty: "null",
18961
- companions: [NOW]
19609
+ companions: [NOW$2]
18962
19610
  }),
18963
19611
  numeric({
18964
19612
  id: "power",
18965
19613
  summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18966
19614
  role: DeviceRole.PowerSensor,
18967
19615
  outputCap: "power-meter",
18968
- outputField: "watts",
19616
+ outputField: "power",
18969
19617
  quantity: "power",
18970
19618
  defaultAggregate: "sum",
18971
19619
  empty: "omit",
18972
- companions: [NOW]
19620
+ companions: [NOW$2]
18973
19621
  }),
18974
19622
  numeric({
18975
19623
  id: "energy",
18976
19624
  summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18977
19625
  role: DeviceRole.EnergySensor,
18978
19626
  outputCap: "power-meter",
18979
- outputField: "kwhTotal",
19627
+ outputField: "energy",
18980
19628
  quantity: "energy",
18981
19629
  defaultAggregate: "sum",
18982
19630
  empty: "omit",
18983
- companions: [NOW]
19631
+ companions: [NOW$2]
18984
19632
  }),
18985
19633
  numeric({
18986
19634
  id: "battery",
18987
19635
  summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18988
19636
  role: DeviceRole.BatterySensor,
18989
19637
  outputCap: "battery",
18990
- outputField: "percentage",
19638
+ outputField: "level",
18991
19639
  quantity: "percentage",
18992
19640
  defaultAggregate: "mean",
18993
19641
  empty: "null",
@@ -19015,7 +19663,7 @@ var GROUP_TEMPLATES = [
19015
19663
  quantity: "device",
19016
19664
  defaultAggregate: "mean",
19017
19665
  empty: "null",
19018
- companions: [NOW]
19666
+ companions: [NOW$2]
19019
19667
  }),
19020
19668
  binary({
19021
19669
  id: "motion",
@@ -19109,6 +19757,11 @@ var GROUP_TEMPLATES = [
19109
19757
  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.",
19110
19758
  deviceType: DeviceType.Switch,
19111
19759
  outputCap: "switch",
19760
+ outputField: "on",
19761
+ member: {
19762
+ cap: "switch",
19763
+ fieldPath: "on"
19764
+ },
19112
19765
  commandCaps: ["switch"]
19113
19766
  }),
19114
19767
  actuator({
@@ -19116,34 +19769,136 @@ var GROUP_TEMPLATES = [
19116
19769
  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.",
19117
19770
  deviceType: DeviceType.Light,
19118
19771
  outputCap: "switch",
19772
+ outputField: "on",
19773
+ member: {
19774
+ cap: "switch",
19775
+ fieldPath: "on"
19776
+ },
19119
19777
  commandCaps: [
19120
19778
  "switch",
19121
19779
  "brightness",
19122
19780
  "color"
19123
19781
  ]
19782
+ }),
19783
+ actuator({
19784
+ id: "fan",
19785
+ 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.",
19786
+ deviceType: DeviceType.Fan,
19787
+ outputCap: "switch",
19788
+ outputField: "on",
19789
+ member: {
19790
+ cap: "switch",
19791
+ fieldPath: "on"
19792
+ },
19793
+ commandCaps: ["switch", "fan-control"]
19794
+ }),
19795
+ actuator({
19796
+ id: "cover",
19797
+ 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.",
19798
+ deviceType: DeviceType.Cover,
19799
+ outputCap: "group-active",
19800
+ outputField: "active",
19801
+ member: {
19802
+ cap: "cover",
19803
+ fieldPath: "state"
19804
+ },
19805
+ activeWhen: [
19806
+ "open",
19807
+ "opening",
19808
+ "stopped"
19809
+ ],
19810
+ commandCaps: ["cover"]
19811
+ }),
19812
+ actuator({
19813
+ id: "valve",
19814
+ 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.",
19815
+ deviceType: DeviceType.Valve,
19816
+ outputCap: "group-active",
19817
+ outputField: "active",
19818
+ member: {
19819
+ cap: "valve",
19820
+ fieldPath: "state"
19821
+ },
19822
+ activeWhen: [
19823
+ "open",
19824
+ "opening",
19825
+ "stopped"
19826
+ ],
19827
+ commandCaps: ["valve"]
19828
+ }),
19829
+ actuator({
19830
+ id: "lock",
19831
+ 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.",
19832
+ deviceType: DeviceType.Lock,
19833
+ outputCap: "group-active",
19834
+ outputField: "active",
19835
+ member: {
19836
+ cap: "lock-control",
19837
+ fieldPath: "state"
19838
+ },
19839
+ activeWhen: ["locked"],
19840
+ commandCaps: ["lock-control"]
19841
+ }),
19842
+ actuator({
19843
+ id: "media-player",
19844
+ 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.",
19845
+ deviceType: DeviceType.MediaPlayer,
19846
+ outputCap: "group-active",
19847
+ outputField: "active",
19848
+ member: {
19849
+ cap: "media-player",
19850
+ fieldPath: "state"
19851
+ },
19852
+ activeWhen: ["playing", "buffering"],
19853
+ commandCaps: ["media-player"]
19854
+ }),
19855
+ actuator({
19856
+ id: "climate",
19857
+ 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.",
19858
+ deviceType: DeviceType.Thermostat,
19859
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
19860
+ outputCap: "group-active",
19861
+ outputField: "active",
19862
+ member: {
19863
+ cap: "climate-control",
19864
+ fieldPath: "mode"
19865
+ },
19866
+ activeWhen: [
19867
+ "heat",
19868
+ "cool",
19869
+ "auto",
19870
+ "heat_cool",
19871
+ "fan_only",
19872
+ "dry"
19873
+ ],
19874
+ commandCaps: ["climate-control"]
19124
19875
  })
19125
19876
  ];
19126
19877
  function actuator(input) {
19127
19878
  return {
19128
- ...input,
19879
+ id: input.id,
19880
+ summary: input.summary,
19881
+ deviceType: input.deviceType,
19882
+ outputCap: input.outputCap,
19883
+ outputField: input.outputField,
19884
+ commandCaps: input.commandCaps,
19129
19885
  family: "actuator",
19130
19886
  role: void 0,
19131
19887
  memberDeviceType: input.deviceType,
19132
- outputField: "on",
19888
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
19889
+ activeWhen: input.activeWhen,
19133
19890
  quantity: "none",
19134
19891
  baseUnit: void 0,
19135
19892
  aggregates: BINARY_AGGREGATES,
19136
19893
  defaultAggregate: "any",
19137
19894
  empty: "offline",
19138
- accept: [{
19139
- cap: "switch",
19140
- fieldPath: "on"
19141
- }],
19895
+ accept: [input.member],
19142
19896
  companions: [CHANGED]
19143
19897
  };
19144
19898
  }
19145
19899
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
19146
19900
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
19901
+ DeviceType.Camera;
19147
19902
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19148
19903
  /**
19149
19904
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -19458,7 +20213,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
19458
20213
  kind: "mutation",
19459
20214
  auth: "admin",
19460
20215
  access: "view"
19461
- });
20216
+ }), method(object({
20217
+ blockId: string().min(1),
20218
+ trace: AutomationRunTraceSchema.optional(),
20219
+ runStartedAt: number().optional(),
20220
+ keepTraces: number().int().min(1).max(100).optional()
20221
+ }), _void(), {
20222
+ kind: "mutation",
20223
+ auth: "admin"
20224
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
20225
+ traces: array(AutomationRunTraceSchema),
20226
+ lastError: string().nullable()
20227
+ }), { auth: "admin" });
19462
20228
  /**
19463
20229
  * `custom-model-registry` — collection cap exposing operator-registered
19464
20230
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -44503,7 +45269,7 @@ var NetworkAccessStatusSchema = object({
44503
45269
  });
44504
45270
  /**
44505
45271
  * Optional, richer endpoint shape returned by providers that expose
44506
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
45272
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44507
45273
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44508
45274
  * the originating provider config (mode + sourcePort) so the
44509
45275
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -44896,51 +45662,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
44896
45662
  kind: "mutation",
44897
45663
  auth: "admin"
44898
45664
  });
44899
- new Set([
44900
- {
44901
- id: "person",
44902
- name: "Person"
44903
- },
44904
- {
44905
- id: "vehicle",
44906
- name: "Vehicle"
44907
- },
44908
- {
44909
- id: "animal",
44910
- name: "Animal"
44911
- },
44912
- {
44913
- id: "package",
44914
- name: "Package"
44915
- }
44916
- ].map((l) => l.id));
44917
- var COCO_TO_MACRO = {
44918
- mapping: {
44919
- person: "person",
44920
- bicycle: "vehicle",
44921
- car: "vehicle",
44922
- motorcycle: "vehicle",
44923
- airplane: "vehicle",
44924
- bus: "vehicle",
44925
- train: "vehicle",
44926
- truck: "vehicle",
44927
- boat: "vehicle",
44928
- bird: "animal",
44929
- cat: "animal",
44930
- dog: "animal",
44931
- horse: "animal",
44932
- sheep: "animal",
44933
- cow: "animal",
44934
- elephant: "animal",
44935
- bear: "animal",
44936
- zebra: "animal",
44937
- giraffe: "animal",
44938
- suitcase: "package",
44939
- backpack: "package",
44940
- handbag: "package"
44941
- },
44942
- preserveOriginal: false
44943
- };
44944
45665
  var AUDIO_MACRO_LABELS = [
44945
45666
  {
44946
45667
  id: "speech",
@@ -47190,7 +47911,20 @@ var NcRuleInputSchema = object({
47190
47911
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47191
47912
  * path does not run Zod defaults.
47192
47913
  */
47193
- groupingSec: number().int().min(5).max(60).optional()
47914
+ groupingSec: number().int().min(5).max(60).optional(),
47915
+ /**
47916
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47917
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47918
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47919
+ * same mark the Library writes, and records whose notification it was and
47920
+ * when. A channel with no buttons gets the same two choices as links.
47921
+ *
47922
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47923
+ * cap path): absent and `false` both mean off, which is what every rule
47924
+ * authored before this field meant. An editor turns it off by writing
47925
+ * `false`, so it needs no `clear` key.
47926
+ */
47927
+ askVerdict: boolean().optional()
47194
47928
  });
47195
47929
  /**
47196
47930
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -58016,9 +58750,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
58016
58750
  */
58017
58751
  var AirQualitySensorStatusSchema = object({
58018
58752
  /** Carbon dioxide concentration in ppm. */
58019
- co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
58753
+ co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
58020
58754
  /** Total volatile organic compounds in ppb. */
58021
- vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
58755
+ voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
58022
58756
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
58023
58757
  pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
58024
58758
  /** Particulate matter ≤ 10 μm in µg/m³. */
@@ -58051,12 +58785,12 @@ var airQualitySensorCapability = {
58051
58785
  * History (D850): recorded series.
58052
58786
  */
58053
58787
  history: { fields: {
58054
- co2Ppm: {
58788
+ co2: {
58055
58789
  kind: "gauge",
58056
58790
  unit: "ppm",
58057
58791
  deadband: 5
58058
58792
  },
58059
- vocPpb: {
58793
+ voc: {
58060
58794
  kind: "gauge",
58061
58795
  unit: "ppb",
58062
58796
  deadband: 5
@@ -58087,7 +58821,7 @@ var airQualitySensorCapability = {
58087
58821
  */
58088
58822
  var AmbientLightSensorStatusSchema = object({
58089
58823
  /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
58090
- lux: number().min(0).nullable().meta(readingMeta("illuminance", {
58824
+ illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
58091
58825
  precision: 0,
58092
58826
  label: "Illuminance"
58093
58827
  })),
@@ -58116,7 +58850,7 @@ var ambientLightSensorCapability = {
58116
58850
  /**
58117
58851
  * History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
58118
58852
  */
58119
- history: { fields: { lux: {
58853
+ history: { fields: { illuminance: {
58120
58854
  kind: "gauge",
58121
58855
  unit: "lx",
58122
58856
  deadbandPct: 10,
@@ -58255,6 +58989,84 @@ var audioMetricsCapability = {
58255
58989
  */
58256
58990
  history: "none"
58257
58991
  };
58992
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58993
+ actionId: string().min(1),
58994
+ variables: record(string(), ActionVariableValueSchema).optional()
58995
+ }), _void(), {
58996
+ kind: "mutation",
58997
+ auth: "admin"
58998
+ }), method(object({ actionId: string().min(1) }), _void(), {
58999
+ kind: "mutation",
59000
+ auth: "admin"
59001
+ });
59002
+ /**
59003
+ * actions — the ONE surface for runnable actions, for the UI and for
59004
+ * automations. Provided by the core-blocks builtin on the hub.
59005
+ *
59006
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
59007
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
59008
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
59009
+ * the run status from the hub's `runs` ledger; there is no catalog here to
59010
+ * drift (D3).
59011
+ *
59012
+ * **Permissions follow the camera, as linked devices do.** An action is linked
59013
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
59014
+ * non-admin may `get` / `run` an action only through a camera they may see that
59015
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
59016
+ * gate sees it) and the provider refuses an action that camera does not link.
59017
+ * An admin, and an in-process caller (an automation block), may run any action.
59018
+ * Every refusal is typed (`[not-linked] …`) and logged.
59019
+ *
59020
+ * A second `run` of an action that is already running is REFUSED — traced and
59021
+ * logged — never queued.
59022
+ */
59023
+ var ActionIdInput = object({ actionId: string().min(1) });
59024
+ var ActionLinksSchema = object({
59025
+ deviceId: number().int().nonnegative(),
59026
+ actionIds: array(string().min(1)).max(100)
59027
+ });
59028
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
59029
+ actionId: string().min(1),
59030
+ deviceId: number().int().nonnegative().optional()
59031
+ }), object({ action: ActionEntrySchema.nullable() }), {
59032
+ role: "viewer",
59033
+ auth: "protected",
59034
+ kind: "query",
59035
+ caller: "required"
59036
+ }), method(object({
59037
+ actionId: string().min(1),
59038
+ variables: record(string(), ActionVariableValueSchema).optional(),
59039
+ wait: boolean().optional(),
59040
+ origin: ActionRunOriginSchema.optional(),
59041
+ deviceId: number().int().nonnegative().optional()
59042
+ }), ActionRunResultSchema, {
59043
+ role: "viewer",
59044
+ auth: "protected",
59045
+ kind: "mutation",
59046
+ caller: "required"
59047
+ }), method(ActionIdInput, _void(), {
59048
+ kind: "mutation",
59049
+ auth: "admin"
59050
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
59051
+ kind: "mutation",
59052
+ auth: "admin"
59053
+ }), method(object({
59054
+ actionId: string().min(1),
59055
+ action: ActionInputSchema.partial()
59056
+ }), object({ action: ActionEntrySchema }), {
59057
+ kind: "mutation",
59058
+ auth: "admin"
59059
+ }), method(ActionIdInput, _void(), {
59060
+ kind: "mutation",
59061
+ auth: "admin"
59062
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
59063
+ role: "viewer",
59064
+ auth: "protected",
59065
+ kind: "query"
59066
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
59067
+ kind: "mutation",
59068
+ auth: "admin"
59069
+ });
58258
59070
  /**
58259
59071
  * Automation-control cap. Models HA `automation.*` entities on
58260
59072
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -58351,7 +59163,7 @@ var BatteryStatusSchema = object({
58351
59163
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
58352
59164
  * declines to band a non-finite reading, which is the correct shape.
58353
59165
  */
58354
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
59166
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
58355
59167
  /**
58356
59168
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
58357
59169
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -58361,7 +59173,7 @@ var BatteryStatusSchema = object({
58361
59173
  * `'unknown'` is the absence of an answer: the provider has not read the
58362
59174
  * power fields yet, or the firmware reported neither. Before it existed,
58363
59175
  * two unreadable fields produced `'none'`, and a camera nobody had reached
58364
- * was drawn "on battery alone" — the same D315 defect `percentage` closed
59176
+ * was drawn "on battery alone" — the same D315 defect `level` closed
58365
59177
  * by going nullable. Consumers SKIP it (no charger state published, no
58366
59178
  * glyph, no HomeKit ChargingState) rather than coerce it either way.
58367
59179
  */
@@ -58385,7 +59197,7 @@ var BatteryStatusSchema = object({
58385
59197
  * that only report charge. A second reading on this cap, not a second
58386
59198
  * `numeric-sensor` on the same device.
58387
59199
  */
58388
- volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
59200
+ voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
58389
59201
  /**
58390
59202
  * Ms epoch of the last time the device PROVED it was reachable — a
58391
59203
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -58419,7 +59231,7 @@ var BatteryStatusSchema = object({
58419
59231
  /**
58420
59232
  * True when the source is a BINARY low-battery indicator (HA
58421
59233
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
58422
- * charge level — `percentage` is then a coarse stand-in (100 = normal,
59234
+ * charge level — `level` is then a coarse stand-in (100 = normal,
58423
59235
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
58424
59236
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
58425
59237
  */
@@ -58432,21 +59244,22 @@ var BATTERY_DEVICE_TYPES = [
58432
59244
  DeviceType.Switch,
58433
59245
  DeviceType.PetFeeder,
58434
59246
  DeviceType.Container,
58435
- DeviceType.Thermostat
59247
+ DeviceType.Thermostat,
59248
+ DeviceType.EventEmitter
58436
59249
  ];
58437
59250
  /** Generic Customize target for the percentage reading (D936). */
58438
59251
  var batteryCustomize = {
58439
59252
  version: 1,
58440
59253
  targets: [{
58441
- id: "percentage",
59254
+ id: "level",
58442
59255
  cardinality: "scalar",
58443
59256
  vocabulary: {
58444
59257
  cap: "battery",
58445
- stateField: "percentage"
59258
+ stateField: "level"
58446
59259
  },
58447
59260
  deviceTypes: BATTERY_DEVICE_TYPES,
58448
59261
  apply: "add",
58449
- path: "percentage",
59262
+ path: "level",
58450
59263
  sources: [{
58451
59264
  kind: "field",
58452
59265
  valueKinds: ["number", "string"],
@@ -58540,7 +59353,7 @@ onStatusChanged: { data: object({
58540
59353
  schema: BatteryStatusSchema,
58541
59354
  kind: "push",
58542
59355
  empty: {
58543
- percentage: null,
59356
+ level: null,
58544
59357
  charging: "unknown",
58545
59358
  sleeping: false,
58546
59359
  lastUpdated: 0
@@ -58567,11 +59380,11 @@ onStatusChanged: { data: object({
58567
59380
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
58568
59381
  */
58569
59382
  history: { fields: {
58570
- percentage: {
59383
+ level: {
58571
59384
  kind: "gauge",
58572
59385
  unit: "%"
58573
59386
  },
58574
- volts: {
59387
+ voltage: {
58575
59388
  kind: "gauge",
58576
59389
  unit: "V"
58577
59390
  }
@@ -58631,14 +59444,14 @@ var binaryCapability = {
58631
59444
  * controls to this single-method surface.
58632
59445
  *
58633
59446
  * The cap is intentionally minimal: a single `setBrightness({deviceId,
58634
- * percentage})` mutation plus the auto-injected `getStatus`. Drivers
59447
+ * level})` mutation plus the auto-injected `getStatus`. Drivers
58635
59448
  * that expose richer controls (color temperature, scenes, schedules)
58636
59449
  * should surface those via the device's `getSettingsUISchema()`
58637
59450
  * instead of bloating this cap.
58638
59451
  */
58639
59452
  var BrightnessStatusSchema = object({
58640
59453
  /** Current level as 0..100 inclusive. Firmware-reported. */
58641
- percentage: number().min(0).max(100).meta(readingMeta("percentage")),
59454
+ level: number().min(0).max(100).meta(readingMeta("percentage")),
58642
59455
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
58643
59456
  lastChangedAt: number()
58644
59457
  });
@@ -58650,7 +59463,7 @@ var brightnessCapability = {
58650
59463
  deviceTypes: [DeviceType.Light],
58651
59464
  methods: { setBrightness: method(object({
58652
59465
  deviceId: number().int().nonnegative(),
58653
- percentage: number().min(0).max(100)
59466
+ level: number().min(0).max(100)
58654
59467
  }), _void(), {
58655
59468
  kind: "mutation",
58656
59469
  auth: "protected"
@@ -58663,7 +59476,7 @@ var brightnessCapability = {
58663
59476
  */
58664
59477
  onBrightnessChanged: { data: object({
58665
59478
  deviceId: number(),
58666
- percentage: number().min(0).max(100),
59479
+ level: number().min(0).max(100),
58667
59480
  lastChangedAt: number()
58668
59481
  }) } },
58669
59482
  status: {
@@ -58686,7 +59499,7 @@ onBrightnessChanged: { data: object({
58686
59499
  /**
58687
59500
  * History (D850): recorded series. Actuator tier: level keeps every change.
58688
59501
  */
58689
- history: { fields: { percentage: {
59502
+ history: { fields: { level: {
58690
59503
  kind: "gauge",
58691
59504
  unit: "%",
58692
59505
  minIntervalMs: 0
@@ -59283,11 +60096,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
59283
60096
  }),
59284
60097
  object({
59285
60098
  mode: literal("mired"),
59286
- mireds: number().int().min(50).max(1e3)
60099
+ colorTemperature: number().int().min(50).max(1e3)
59287
60100
  })
59288
60101
  ]);
59289
60102
  var ColorStatusSchema = object({
59290
- /** Active color mode — which of `rgb` / `hsv` / `mireds` reflects
60103
+ /** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
59291
60104
  * the bulb's current state. */
59292
60105
  mode: _enum([
59293
60106
  "rgb",
@@ -59299,7 +60112,7 @@ var ColorStatusSchema = object({
59299
60112
  /** Populated when `mode === 'hsv'`. */
59300
60113
  hsv: HsvTripletSchema.optional(),
59301
60114
  /** Populated when `mode === 'mired'`. */
59302
- mireds: number().int().optional().meta(readingMeta("colour-temperature")),
60115
+ colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
59303
60116
  /** Ms epoch of the last operator-driven change. */
59304
60117
  lastChangedAt: number()
59305
60118
  });
@@ -59331,7 +60144,7 @@ onColorChanged: { data: object({
59331
60144
  ]),
59332
60145
  rgb: RgbTripletSchema.optional(),
59333
60146
  hsv: HsvTripletSchema.optional(),
59334
- mireds: number().int().optional(),
60147
+ colorTemperature: number().int().optional(),
59335
60148
  lastChangedAt: number()
59336
60149
  }) } },
59337
60150
  status: {
@@ -60208,11 +61021,11 @@ var DishwasherStatusSchema = object({
60208
61021
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
60209
61022
  availablePrograms: array(string()),
60210
61023
  /** Seconds remaining on the active cycle, or null when not running / not known. */
60211
- remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
61024
+ remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60212
61025
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
60213
- delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
61026
+ delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60214
61027
  /** Cycle completion 0..100, or null when not applicable. */
60215
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61028
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60216
61029
  /** True when the door is reported open; null when the door state is not known. */
60217
61030
  doorOpen: boolean().nullable(),
60218
61031
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -60431,14 +61244,14 @@ var dishwasherCapability = {
60431
61244
  runtimeState: DishwasherStatusSchema,
60432
61245
  durability: "session",
60433
61246
  history: { fields: {
60434
- progressPercent: {
61247
+ progress: {
60435
61248
  kind: "gauge",
60436
61249
  unit: "%",
60437
61250
  deadband: 2
60438
61251
  },
60439
61252
  state: { kind: "enum" },
60440
61253
  operationState: { kind: "enum" },
60441
- remainingSeconds: {
61254
+ remaining: {
60442
61255
  kind: "gauge",
60443
61256
  unit: "s",
60444
61257
  minIntervalMs: 6e4
@@ -61029,7 +61842,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
61029
61842
  var FanControlStatusSchema = object({
61030
61843
  /** Active speed as 0..100 inclusive. Null when the device has no
61031
61844
  * speed surface (single-speed fan). */
61032
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61845
+ speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61033
61846
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
61034
61847
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
61035
61848
  percentageStep: number().positive().optional().meta(readingMeta("percentage")),
@@ -61056,7 +61869,7 @@ var fanControlCapability = {
61056
61869
  methods: {
61057
61870
  setPercentage: method(object({
61058
61871
  deviceId: number().int().nonnegative(),
61059
- percentage: number().min(0).max(100)
61872
+ speed: number().min(0).max(100)
61060
61873
  }), _void(), {
61061
61874
  kind: "mutation",
61062
61875
  auth: "protected"
@@ -61089,7 +61902,7 @@ var fanControlCapability = {
61089
61902
  },
61090
61903
  /**
61091
61904
  * Runtime-state slice — mirrored by the kernel. UI fan speed
61092
- * sliders read `percentage` for live updates.
61905
+ * sliders read `speed` for live updates.
61093
61906
  */
61094
61907
  runtimeState: FanControlStatusSchema,
61095
61908
  /**
@@ -61103,7 +61916,7 @@ var fanControlCapability = {
61103
61916
  * History (D850): recorded series. Actuator tier: speed keeps every change.
61104
61917
  */
61105
61918
  history: { fields: {
61106
- percentage: {
61919
+ speed: {
61107
61920
  kind: "gauge",
61108
61921
  unit: "%",
61109
61922
  minIntervalMs: 0
@@ -61292,7 +62105,17 @@ var groupCommandsCapability = {
61292
62105
  scope: "device",
61293
62106
  deviceNative: true,
61294
62107
  mode: "singleton",
61295
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
62108
+ deviceTypes: [
62109
+ DeviceType.Switch,
62110
+ DeviceType.Light,
62111
+ DeviceType.Fan,
62112
+ DeviceType.Cover,
62113
+ DeviceType.Valve,
62114
+ DeviceType.Lock,
62115
+ DeviceType.MediaPlayer,
62116
+ DeviceType.Thermostat,
62117
+ DeviceType.Climate
62118
+ ],
61296
62119
  methods: {},
61297
62120
  status: {
61298
62121
  schema: GroupCommandsSchema,
@@ -61310,6 +62133,49 @@ var groupCommandsCapability = {
61310
62133
  history: "none"
61311
62134
  };
61312
62135
  /**
62136
+ * Whether a helper actuator group counts as active (D944): at least one
62137
+ * included member is, or every included member is. What "active" means is the
62138
+ * type's: a cover is open, a lock is locked, a media player is playing, a
62139
+ * climate device is not off. A group of lights, switches and fans reports
62140
+ * `switch.on` instead; this cap is for the types whose own state is not a
62141
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
62142
+ * empty included set takes the device offline, never `active: false`.
62143
+ */
62144
+ var GroupActiveSchema = object({
62145
+ active: boolean().meta({ label: "Active" }),
62146
+ /** Ms epoch when the slice was last updated. */
62147
+ lastChangedAt: number()
62148
+ });
62149
+ var groupActiveCapability = {
62150
+ name: "group-active",
62151
+ scope: "device",
62152
+ deviceNative: true,
62153
+ mode: "singleton",
62154
+ deviceTypes: [
62155
+ DeviceType.Cover,
62156
+ DeviceType.Valve,
62157
+ DeviceType.Lock,
62158
+ DeviceType.MediaPlayer,
62159
+ DeviceType.Climate,
62160
+ DeviceType.Thermostat
62161
+ ],
62162
+ methods: {},
62163
+ status: {
62164
+ schema: GroupActiveSchema,
62165
+ kind: "push"
62166
+ },
62167
+ runtimeState: GroupActiveSchema,
62168
+ /**
62169
+ * Runtime-state durability: **session** — recomputed from the members on the
62170
+ * next source event; a restored value would outlive the members it named.
62171
+ *
62172
+ * See `RuntimeStateDurability`. Enforced by
62173
+ * `scripts/check-runtime-state-durability.ts`.
62174
+ */
62175
+ durability: "session",
62176
+ history: "none"
62177
+ };
62178
+ /**
61313
62179
  * How many members of a helper group answered (D944). Counts are integers,
61314
62180
  * not measurements: zero included is a real count, never a stand-in reading.
61315
62181
  * The reading itself stays on the output capability. History is none — the
@@ -61340,7 +62206,14 @@ var groupStatusCapability = {
61340
62206
  deviceTypes: [
61341
62207
  DeviceType.Sensor,
61342
62208
  DeviceType.Switch,
61343
- DeviceType.Light
62209
+ DeviceType.Light,
62210
+ DeviceType.Fan,
62211
+ DeviceType.Cover,
62212
+ DeviceType.Valve,
62213
+ DeviceType.Lock,
62214
+ DeviceType.MediaPlayer,
62215
+ DeviceType.Thermostat,
62216
+ DeviceType.Climate
61344
62217
  ],
61345
62218
  methods: {},
61346
62219
  status: {
@@ -61488,7 +62361,7 @@ var humidifierCapability = {
61488
62361
  */
61489
62362
  var HumiditySensorStatusSchema = object({
61490
62363
  /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
61491
- percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
62364
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
61492
62365
  precision: 0,
61493
62366
  label: "Humidity"
61494
62367
  })),
@@ -61517,7 +62390,7 @@ var humiditySensorCapability = {
61517
62390
  /**
61518
62391
  * History (D850): recorded series.
61519
62392
  */
61520
- history: { fields: { percent: {
62393
+ history: { fields: { humidity: {
61521
62394
  kind: "gauge",
61522
62395
  unit: "%",
61523
62396
  deadband: .5
@@ -62157,7 +63030,7 @@ var LawnMowerControlStatusSchema = object({
62157
63030
  batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62158
63031
  /** 0..100 mowing-completion percentage of the current task, or null when no
62159
63032
  * task is active / progress is unavailable. */
62160
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63033
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62161
63034
  /** Current device/event code (dynamic — mostly status, sometimes an error),
62162
63035
  * or null when unknown. */
62163
63036
  currentCode: number().nullable().meta(readingMeta("none")),
@@ -62613,7 +63486,14 @@ var lockControlCapability = {
62613
63486
  }),
62614
63487
  unlock: method(object({
62615
63488
  deviceId: number().int().nonnegative(),
62616
- code: string().min(1).optional()
63489
+ code: string().min(1).optional(),
63490
+ /**
63491
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
63492
+ * with one call is refused unless the caller says it was confirmed. A
63493
+ * single lock ignores it. The refusal lives in the callee, so no
63494
+ * automation or API caller reaches the doors by skipping a dialog.
63495
+ */
63496
+ confirm: boolean().optional()
62617
63497
  }), _void(), {
62618
63498
  kind: "mutation",
62619
63499
  auth: "protected"
@@ -62650,6 +63530,44 @@ var lockControlCapability = {
62650
63530
  volatileStateFields: ["lastChangedAt"]
62651
63531
  };
62652
63532
  /**
63533
+ * A monotonic total between resets (D944). Energy uses `power-meter.energy`
63534
+ * so dashboards keep one series; every other instantaneous reading lands here.
63535
+ * `value` is the time integral in the source quantity's accumulated base
63536
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
63537
+ */
63538
+ var MeterStatusSchema = object({
63539
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
63540
+ lastFetchedAt: number()
63541
+ });
63542
+ var meterCapability = {
63543
+ name: "meter",
63544
+ scope: "device",
63545
+ deviceNative: true,
63546
+ mode: "singleton",
63547
+ deviceTypes: [DeviceType.Sensor],
63548
+ methods: {},
63549
+ status: {
63550
+ schema: MeterStatusSchema,
63551
+ kind: "push"
63552
+ },
63553
+ runtimeState: MeterStatusSchema,
63554
+ /**
63555
+ * Runtime-state durability: **restored** — the total is a cache of the
63556
+ * history integral. A restart shows it only until the history pass
63557
+ * recomputes the open period; it is not an in-memory buffer.
63558
+ *
63559
+ * See `RuntimeStateDurability`. Enforced by
63560
+ * `scripts/check-runtime-state-durability.ts`.
63561
+ */
63562
+ durability: "restored",
63563
+ history: { fields: { value: {
63564
+ kind: "counter",
63565
+ deadband: .001,
63566
+ minIntervalMs: 6e4
63567
+ } } },
63568
+ volatileStateFields: ["lastFetchedAt"]
63569
+ };
63570
+ /**
62653
63571
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
62654
63572
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
62655
63573
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -62706,10 +63624,10 @@ var MediaPlayerStatusSchema = object({
62706
63624
  currentMedia: MediaInfoSchema.nullable(),
62707
63625
  /** Current playback position in ms. Null when not seekable or
62708
63626
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
62709
- positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
63627
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62710
63628
  /** Total duration of the current media in ms. Null when unknown
62711
63629
  * (live stream) or nothing is playing. */
62712
- durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
63630
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62713
63631
  /** Shuffle toggle. Null when no shuffle surface.
62714
63632
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
62715
63633
  shuffle: boolean().nullable(),
@@ -62748,7 +63666,7 @@ var mediaPlayerCapability = {
62748
63666
  }),
62749
63667
  seek: method(object({
62750
63668
  deviceId: number().int().nonnegative(),
62751
- positionMs: number().int().nonnegative()
63669
+ position: number().int().nonnegative()
62752
63670
  }), _void(), {
62753
63671
  kind: "mutation",
62754
63672
  auth: "protected"
@@ -62820,7 +63738,7 @@ var mediaPlayerCapability = {
62820
63738
  /**
62821
63739
  * History (D850): volume (a level an operator or an automation sets; the
62822
63740
  * default 10 s floor folds a slider drag into its end value) and mute.
62823
- * `positionMs` is not recorded: it moves continuously while playing.
63741
+ * `position` is not recorded: it moves continuously while playing.
62824
63742
  */
62825
63743
  history: { fields: {
62826
63744
  volumeLevel: {
@@ -63782,9 +64700,9 @@ var NetworkLinkStatusSchema = object({
63782
64700
  * one whose reading has not landed must not be drawn at 0 %. Consumers
63783
64701
  * SKIP a null rather than coerce it.
63784
64702
  */
63785
- signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64703
+ signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63786
64704
  /** Raw received signal strength in dBm, when the firmware reports one. */
63787
- rssiDbm: number().optional().meta(readingMeta("signal-level")),
64705
+ rssi: number().optional().meta(readingMeta("signal-level")),
63788
64706
  /** Network name of a wireless link, when the firmware reports it. */
63789
64707
  ssid: string().optional(),
63790
64708
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
@@ -63793,7 +64711,7 @@ var NetworkLinkStatusSchema = object({
63793
64711
  /** The slice a provider seeds before its first read: nothing is known yet. */
63794
64712
  var NETWORK_LINK_UNKNOWN = {
63795
64713
  type: "unknown",
63796
- signalPercent: null,
64714
+ signal: null,
63797
64715
  lastUpdated: 0
63798
64716
  };
63799
64717
  /**
@@ -63810,9 +64728,9 @@ function signalPercentFromBars(bars, maxBars) {
63810
64728
  * below is 0 %, -50 dBm and above is 100 %, linear between. Non-finite or
63811
64729
  * positive input is not an RSSI: `null`.
63812
64730
  */
63813
- function signalPercentFromRssi(rssiDbm) {
63814
- if (rssiDbm === void 0 || !Number.isFinite(rssiDbm) || rssiDbm > 0) return null;
63815
- return Math.round((Math.min(-50, Math.max(-100, rssiDbm)) + 100) * 2);
64731
+ function signalPercentFromRssi(rssi) {
64732
+ if (rssi === void 0 || !Number.isFinite(rssi) || rssi > 0) return null;
64733
+ return Math.round((Math.min(-50, Math.max(-100, rssi)) + 100) * 2);
63816
64734
  }
63817
64735
  var networkLinkCapability = {
63818
64736
  name: "network-link",
@@ -63863,13 +64781,13 @@ onStatusChanged: { data: object({
63863
64781
  * History (D850): recorded series.
63864
64782
  */
63865
64783
  history: { fields: {
63866
- signalPercent: {
64784
+ signal: {
63867
64785
  kind: "gauge",
63868
64786
  unit: "%",
63869
64787
  deadband: 3,
63870
64788
  minIntervalMs: 6e4
63871
64789
  },
63872
- rssiDbm: {
64790
+ rssi: {
63873
64791
  kind: "gauge",
63874
64792
  unit: "dBm",
63875
64793
  deadband: 2,
@@ -64660,10 +65578,20 @@ var PetFeederDeviceStatusSchema = _enum([
64660
65578
  ]);
64661
65579
  var gramsPortion = number().int().min(4).max(200);
64662
65580
  var PetFeederStatusSchema = object({
64663
- /** Food currently in the bowl (grams). Null when the device has not
64664
- * reported a reading yet. On dual-hopper models this is the combined
64665
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
65581
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
65582
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
65583
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
65584
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
65585
+ * the device has not reported yet. */
64666
65586
  foodLevel: number().nullable().meta(readingMeta("none")),
65587
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
65588
+ * feeder's own units. Null when the model reports none. */
65589
+ bowlFood: number().nullable().meta(readingMeta("none")),
65590
+ /** True while the pet is eating (`state.eating`). */
65591
+ eating: boolean(),
65592
+ /** Whether `markFoodReplenished` does anything on this model. When false
65593
+ * the client must not offer it. */
65594
+ supportsMarkRefilled: boolean(),
64667
65595
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
64668
65596
  * single-hopper models. */
64669
65597
  food1: number().nullable().meta(readingMeta("none")),
@@ -64671,8 +65599,8 @@ var PetFeederStatusSchema = object({
64671
65599
  * single-hopper models. */
64672
65600
  food2: number().nullable().meta(readingMeta("none")),
64673
65601
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
64674
- * (`device_class: problem`, on = low). True when the bowl is empty /
64675
- * below the feeder's low threshold. */
65602
+ * (`device_class: problem`, on = low). True when the tank reports empty,
65603
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
64676
65604
  lowFood: boolean(),
64677
65605
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
64678
65606
  * device has no battery reading. */
@@ -64740,7 +65668,9 @@ var petFeederCapability = {
64740
65668
  kind: "mutation",
64741
65669
  auth: "protected"
64742
65670
  }),
64743
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
65671
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
65672
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
65673
+ * change any sensor reading. */
64744
65674
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
64745
65675
  kind: "mutation",
64746
65676
  auth: "protected"
@@ -64813,6 +65743,7 @@ var petFeederCapability = {
64813
65743
  */
64814
65744
  history: { fields: {
64815
65745
  foodLevel: { kind: "gauge" },
65746
+ bowlFood: { kind: "gauge" },
64816
65747
  food1: { kind: "gauge" },
64817
65748
  food2: { kind: "gauge" },
64818
65749
  batteryPower: {
@@ -65222,27 +66153,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
65222
66153
  * in real-world devices.
65223
66154
  */
65224
66155
  var PowerMeterStatusSchema = object({
65225
- /** Instantaneous power draw in watts. */
65226
- watts: number().optional().meta(readingMeta("power", { label: "Power" })),
66156
+ /** Instantaneous power draw (watts). */
66157
+ power: number().optional().meta(readingMeta("power", { label: "Power" })),
65227
66158
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
65228
- kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
65229
- /** Voltage in volts. */
65230
- volts: number().optional().meta(readingMeta("voltage")),
66159
+ energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
66160
+ /** Voltage (volts). */
66161
+ voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
65231
66162
  /** Current in amperes. */
65232
- amps: number().optional().meta(readingMeta("current")),
66163
+ current: number().optional().meta(readingMeta("current", { label: "Current" })),
65233
66164
  /** Apparent power in volt-amperes. */
65234
- va: number().optional().meta(readingMeta("apparent-power")),
66165
+ apparentPower: number().optional().meta(readingMeta("apparent-power")),
65235
66166
  /** Power factor (0..1) if reported. */
65236
66167
  powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
65237
66168
  /** Ms epoch when the slice was last updated. */
65238
66169
  lastFetchedAt: number()
65239
66170
  });
66171
+ /**
66172
+ * Loads that draw metered mains power (D951). The reading is a feature of the
66173
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
66174
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
66175
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
66176
+ * camera and the like are left out: their draw is not what anyone meters.
66177
+ */
66178
+ var POWER_METER_DEVICE_TYPES = [
66179
+ DeviceType.Sensor,
66180
+ DeviceType.Switch,
66181
+ DeviceType.Light,
66182
+ DeviceType.Fan,
66183
+ DeviceType.Climate,
66184
+ DeviceType.Thermostat,
66185
+ DeviceType.Humidifier,
66186
+ DeviceType.WaterHeater,
66187
+ DeviceType.MediaPlayer,
66188
+ DeviceType.Washer,
66189
+ DeviceType.Dryer,
66190
+ DeviceType.Dishwasher,
66191
+ DeviceType.Vacuum,
66192
+ DeviceType.LawnMower,
66193
+ DeviceType.PetFeeder
66194
+ ];
66195
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
66196
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
66197
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
66198
+ function powerTarget(id, unit) {
66199
+ return {
66200
+ id,
66201
+ cardinality: "scalar",
66202
+ vocabulary: {
66203
+ cap: "power-meter",
66204
+ stateField: id
66205
+ },
66206
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
66207
+ apply: "add",
66208
+ path: id,
66209
+ sources: [{
66210
+ kind: "numeric",
66211
+ requiredUnit: unit,
66212
+ transforms: [{ kind: "identity" }]
66213
+ }],
66214
+ companions: [{
66215
+ kind: "clock",
66216
+ path: "lastFetchedAt",
66217
+ when: "always"
66218
+ }],
66219
+ sourceReport: { status: "assignment-status" },
66220
+ advancedExpression: true
66221
+ };
66222
+ }
66223
+ /** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
66224
+ var powerMeterCustomize = {
66225
+ version: 1,
66226
+ targets: [
66227
+ powerTarget("power", "W"),
66228
+ powerTarget("energy", "kWh"),
66229
+ powerTarget("voltage", "V"),
66230
+ powerTarget("current", "A")
66231
+ ]
66232
+ };
65240
66233
  var powerMeterCapability = {
65241
66234
  name: "power-meter",
65242
66235
  scope: "device",
65243
66236
  deviceNative: true,
65244
66237
  mode: "singleton",
65245
- deviceTypes: [DeviceType.Sensor],
66238
+ deviceTypes: POWER_METER_DEVICE_TYPES,
65246
66239
  methods: {},
65247
66240
  status: {
65248
66241
  schema: PowerMeterStatusSchema,
@@ -65250,41 +66243,41 @@ var powerMeterCapability = {
65250
66243
  },
65251
66244
  runtimeState: PowerMeterStatusSchema,
65252
66245
  /**
65253
- * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
66246
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
65254
66247
  *
65255
66248
  * See `RuntimeStateDurability`. Enforced by
65256
66249
  * `scripts/check-runtime-state-durability.ts`.
65257
66250
  */
65258
66251
  durability: "restored",
65259
66252
  /**
65260
- * History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
66253
+ * History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
65261
66254
  */
65262
66255
  history: { fields: {
65263
- watts: {
66256
+ power: {
65264
66257
  kind: "gauge",
65265
66258
  unit: "W",
65266
66259
  deadband: 1,
65267
66260
  minIntervalMs: 3e4
65268
66261
  },
65269
- kwhTotal: {
66262
+ energy: {
65270
66263
  kind: "counter",
65271
66264
  unit: "kWh",
65272
66265
  deadband: .01,
65273
66266
  minIntervalMs: 6e4
65274
66267
  },
65275
- volts: {
66268
+ voltage: {
65276
66269
  kind: "gauge",
65277
66270
  unit: "V",
65278
66271
  deadband: 1,
65279
66272
  minIntervalMs: 3e4
65280
66273
  },
65281
- amps: {
66274
+ current: {
65282
66275
  kind: "gauge",
65283
66276
  unit: "A",
65284
66277
  deadband: .05,
65285
66278
  minIntervalMs: 3e4
65286
66279
  },
65287
- va: {
66280
+ apparentPower: {
65288
66281
  kind: "gauge",
65289
66282
  unit: "VA",
65290
66283
  deadband: 1,
@@ -65299,7 +66292,8 @@ var powerMeterCapability = {
65299
66292
  } },
65300
66293
  /** Clock fields: written, but excluded from the compare that decides
65301
66294
  * whether persisting is worth a SQLite commit. */
65302
- volatileStateFields: ["lastFetchedAt"]
66295
+ volatileStateFields: ["lastFetchedAt"],
66296
+ customize: powerMeterCustomize
65303
66297
  };
65304
66298
  /**
65305
66299
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -65333,9 +66327,6 @@ var PresenceStatusSchema = object({
65333
66327
  location: string().nullable(),
65334
66328
  /** GPS coordinates when available. Null otherwise. */
65335
66329
  gps: GpsLocationSchema.nullable(),
65336
- /** Optional battery level of the tracking device (0..100). Null
65337
- * when not reported. */
65338
- batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
65339
66330
  /** Ms epoch when the slice was last updated. */
65340
66331
  lastChangedAt: number()
65341
66332
  });
@@ -65364,14 +66355,7 @@ var presenceCapability = {
65364
66355
  * `scripts/check-runtime-state-durability.ts`.
65365
66356
  */
65366
66357
  durability: "restored",
65367
- /**
65368
- * History (D850): recorded series.
65369
- */
65370
- history: { fields: { batteryPercent: {
65371
- kind: "gauge",
65372
- unit: "%",
65373
- deadband: 1
65374
- } } },
66358
+ history: "none",
65375
66359
  /** Clock fields: written, but excluded from the compare that decides
65376
66360
  * whether persisting is worth a SQLite commit. */
65377
66361
  volatileStateFields: ["lastChangedAt"]
@@ -65386,7 +66370,7 @@ var presenceCapability = {
65386
66370
  */
65387
66371
  var PressureSensorStatusSchema = object({
65388
66372
  /** Current pressure in hPa. `null` when the sample was refused (D393). */
65389
- hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
66373
+ pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
65390
66374
  /** Ms epoch when the slice was last updated. */
65391
66375
  lastFetchedAt: number()
65392
66376
  });
@@ -65412,7 +66396,7 @@ var pressureSensorCapability = {
65412
66396
  /**
65413
66397
  * History (D850): recorded series.
65414
66398
  */
65415
- history: { fields: { hpa: {
66399
+ history: { fields: { pressure: {
65416
66400
  kind: "gauge",
65417
66401
  unit: "hPa",
65418
66402
  deadband: .1
@@ -66092,6 +67076,8 @@ var runningCustomize = {
66092
67076
  stateField: "running"
66093
67077
  },
66094
67078
  deviceTypes: [
67079
+ DeviceType.Washer,
67080
+ DeviceType.Dryer,
66095
67081
  DeviceType.Dishwasher,
66096
67082
  DeviceType.Vacuum,
66097
67083
  DeviceType.LawnMower,
@@ -68973,7 +69959,8 @@ var switchCapability = {
68973
69959
  deviceTypes: [
68974
69960
  DeviceType.Switch,
68975
69961
  DeviceType.Siren,
68976
- DeviceType.Light
69962
+ DeviceType.Light,
69963
+ DeviceType.Fan
68977
69964
  ],
68978
69965
  methods: { setState: method(object({
68979
69966
  deviceId: number(),
@@ -69612,7 +70599,7 @@ var tamperCapability = {
69612
70599
  */
69613
70600
  var TemperatureSensorStatusSchema = object({
69614
70601
  /** Current temperature in Celsius. `null` when the sample was refused (D393). */
69615
- celsius: number().nullable().meta(readingMeta("temperature", {
70602
+ temperature: number().nullable().meta(readingMeta("temperature", {
69616
70603
  precision: 1,
69617
70604
  label: "Temperature"
69618
70605
  })),
@@ -69641,7 +70628,7 @@ var temperatureSensorCapability = {
69641
70628
  /**
69642
70629
  * History (D850): recorded series.
69643
70630
  */
69644
- history: { fields: { celsius: {
70631
+ history: { fields: { temperature: {
69645
70632
  kind: "gauge",
69646
70633
  unit: "°C",
69647
70634
  deadband: .1
@@ -69938,7 +70925,7 @@ var VacuumControlStatusSchema = object({
69938
70925
  /** Speed tokens the hardware accepts — drives the UI selector. */
69939
70926
  availableFanSpeeds: array(string()),
69940
70927
  /** 0..100 cleaning-completion percentage of the current task, or null. */
69941
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
70928
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
69942
70929
  /** Current error code (0 / null = no error). */
69943
70930
  errorCode: number().nullable().meta(readingMeta("none")),
69944
70931
  /** Human label for {@link errorCode}, or null when none / undecodable. */
@@ -71319,8 +72306,9 @@ function withClipEventLabels(provider, deps) {
71319
72306
  listClips
71320
72307
  };
71321
72308
  }
72309
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
71322
72310
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
71323
- 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;
72311
+ 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;
71324
72312
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
71325
72313
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
71326
72314
  new Set(CAP_NAMES);
@@ -71664,6 +72652,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
71664
72652
  featureProbe: featureProbeCapability,
71665
72653
  flood: floodCapability,
71666
72654
  gas: gasCapability,
72655
+ groupActive: groupActiveCapability,
71667
72656
  groupCommands: groupCommandsCapability,
71668
72657
  groupStatus: groupStatusCapability,
71669
72658
  heatingValve: heatingValveCapability,
@@ -71675,6 +72664,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
71675
72664
  lawnMowerControl: lawnMowerControlCapability,
71676
72665
  lockControl: lockControlCapability,
71677
72666
  mediaPlayer: mediaPlayerCapability,
72667
+ meter: meterCapability,
71678
72668
  motion: motionCapability,
71679
72669
  motionTrigger: motionTriggerCapability,
71680
72670
  motionZones: motionZonesCapability,
@@ -71737,7 +72727,7 @@ var BaseDevice = class {
71737
72727
  * `device-local-state.ts`. Drivers access via:
71738
72728
  *
71739
72729
  * `this.state.battery.sleeping = true` // patches the battery slice
71740
- * `const pct = this.state.battery.percentage` // reads the battery slice
72730
+ * `const pct = this.state.battery.level` // reads the battery slice
71741
72731
  * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
71742
72732
  *
71743
72733
  * Adding a new cap with `runtimeState:` automatically extends this
@@ -71777,7 +72767,7 @@ var BaseDevice = class {
71777
72767
  * shape; drivers don't redeclare it, they just write through.
71778
72768
  *
71779
72769
  * Read: `this.runtimeState.getCapState('battery')` →
71780
- * `{percentage, charging, sleeping, lastUpdated}` for any
72770
+ * `{level, charging, sleeping, lastUpdated}` for any
71781
72771
  * provider that registers `batteryCapability`.
71782
72772
  * Write: `this.runtimeState.setCapState('battery', { … })`.
71783
72773
  *
@@ -72934,7 +73924,7 @@ var BaseDeviceProvider = class extends BaseAddon {
72934
73924
  return toDeviceSummary(device, this.addonId);
72935
73925
  }
72936
73926
  };
72937
- 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;
73927
+ 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;
72938
73928
  new Set(Object.values(DeviceType));
72939
73929
  Object.freeze({
72940
73930
  "accessories.getStatus": {
@@ -72951,6 +73941,104 @@ Object.freeze({
72951
73941
  access: "create",
72952
73942
  role: "admin"
72953
73943
  },
73944
+ "actions.create": {
73945
+ capName: "actions",
73946
+ capScope: "system",
73947
+ addonId: null,
73948
+ access: "create",
73949
+ role: "admin"
73950
+ },
73951
+ "actions.delete": {
73952
+ capName: "actions",
73953
+ capScope: "system",
73954
+ addonId: null,
73955
+ access: "delete",
73956
+ role: "admin"
73957
+ },
73958
+ "actions.get": {
73959
+ capName: "actions",
73960
+ capScope: "system",
73961
+ addonId: null,
73962
+ access: "view",
73963
+ role: "viewer"
73964
+ },
73965
+ "actions.getLinks": {
73966
+ capName: "actions",
73967
+ capScope: "system",
73968
+ addonId: null,
73969
+ access: "view",
73970
+ role: "admin"
73971
+ },
73972
+ "actions.getTraces": {
73973
+ capName: "actions",
73974
+ capScope: "system",
73975
+ addonId: null,
73976
+ access: "view",
73977
+ role: "admin"
73978
+ },
73979
+ "actions.list": {
73980
+ capName: "actions",
73981
+ capScope: "system",
73982
+ addonId: null,
73983
+ access: "view",
73984
+ role: "admin"
73985
+ },
73986
+ "actions.listForCamera": {
73987
+ capName: "actions",
73988
+ capScope: "system",
73989
+ addonId: null,
73990
+ access: "view",
73991
+ role: "viewer"
73992
+ },
73993
+ "actions.run": {
73994
+ capName: "actions",
73995
+ capScope: "system",
73996
+ addonId: null,
73997
+ access: "create",
73998
+ role: "viewer"
73999
+ },
74000
+ "actions.setLinks": {
74001
+ capName: "actions",
74002
+ capScope: "system",
74003
+ addonId: null,
74004
+ access: "create",
74005
+ role: "admin"
74006
+ },
74007
+ "actions.stop": {
74008
+ capName: "actions",
74009
+ capScope: "system",
74010
+ addonId: null,
74011
+ access: "create",
74012
+ role: "admin"
74013
+ },
74014
+ "actions.update": {
74015
+ capName: "actions",
74016
+ capScope: "system",
74017
+ addonId: null,
74018
+ access: "create",
74019
+ role: "admin"
74020
+ },
74021
+ "actionSource.listActions": {
74022
+ capName: "action-source",
74023
+ capScope: "system",
74024
+ addonId: null,
74025
+ access: "view",
74026
+ role: "admin"
74027
+ },
74028
+ "actionSource.runAction": {
74029
+ capName: "action-source",
74030
+ capScope: "system",
74031
+ addonId: null,
74032
+ access: "create",
74033
+ role: "admin"
74034
+ },
74035
+ "actionSource.stopAction": {
74036
+ capName: "action-source",
74037
+ capScope: "system",
74038
+ addonId: null,
74039
+ access: "create",
74040
+ role: "admin"
74041
+ },
72954
74042
  "addonPages.listPages": {
72955
74043
  capName: "addon-pages",
72956
74044
  capScope: "system",
@@ -74232,6 +75320,13 @@ Object.freeze({
74232
75320
  access: "view",
74233
75321
  role: "admin"
74234
75322
  },
75323
+ "coreBlocks.getAutomationRunState": {
75324
+ capName: "core-blocks",
75325
+ capScope: "system",
75326
+ addonId: null,
75327
+ access: "view",
75328
+ role: "admin"
75329
+ },
74235
75330
  "coreBlocks.getBlockSettings": {
74236
75331
  capName: "core-blocks",
74237
75332
  capScope: "system",
@@ -74253,6 +75348,13 @@ Object.freeze({
74253
75348
  access: "view",
74254
75349
  role: "admin"
74255
75350
  },
75351
+ "coreBlocks.getTraces": {
75352
+ capName: "core-blocks",
75353
+ capScope: "system",
75354
+ addonId: null,
75355
+ access: "view",
75356
+ role: "admin"
75357
+ },
74256
75358
  "coreBlocks.getTypeDefs": {
74257
75359
  capName: "core-blocks",
74258
75360
  capScope: "system",
@@ -74288,6 +75390,13 @@ Object.freeze({
74288
75390
  access: "create",
74289
75391
  role: "admin"
74290
75392
  },
75393
+ "coreBlocks.reportAutomationRun": {
75394
+ capName: "core-blocks",
75395
+ capScope: "system",
75396
+ addonId: null,
75397
+ access: "create",
75398
+ role: "admin"
75399
+ },
74291
75400
  "coreBlocks.restart": {
74292
75401
  capName: "core-blocks",
74293
75402
  capScope: "system",
@@ -76157,6 +77266,13 @@ Object.freeze({
76157
77266
  access: "view",
76158
77267
  role: "viewer"
76159
77268
  },
77269
+ "groupActive.getStatus": {
77270
+ capName: "group-active",
77271
+ capScope: "device",
77272
+ addonId: null,
77273
+ access: "view",
77274
+ role: "viewer"
77275
+ },
76160
77276
  "groupCommands.getStatus": {
76161
77277
  capName: "group-commands",
76162
77278
  capScope: "device",
@@ -77123,6 +78239,13 @@ Object.freeze({
77123
78239
  access: "create",
77124
78240
  role: "admin"
77125
78241
  },
78242
+ "meter.getStatus": {
78243
+ capName: "meter",
78244
+ capScope: "device",
78245
+ addonId: null,
78246
+ access: "view",
78247
+ role: "viewer"
78248
+ },
77126
78249
  "metricsProvider.collectSnapshot": {
77127
78250
  capName: "metrics-provider",
77128
78251
  capScope: "system",
@@ -82847,7 +83970,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82847
83970
  label: t("Full brightness", "Luminosità massima"),
82848
83971
  phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
82849
83972
  icon: "sun",
82850
- args: { percentage: 100 }
83973
+ args: { level: 100 }
82851
83974
  },
82852
83975
  {
82853
83976
  id: "brightness.half",
@@ -82855,7 +83978,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82855
83978
  label: t("Half brightness", "Luminosità a metà"),
82856
83979
  phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
82857
83980
  icon: "sun-dim",
82858
- args: { percentage: 50 }
83981
+ args: { level: 50 }
82859
83982
  },
82860
83983
  {
82861
83984
  id: "brightness.off",
@@ -82863,16 +83986,16 @@ var ACTUATOR_CAP_VOCABULARY = {
82863
83986
  label: t("Brightness off", "Luminosità a zero"),
82864
83987
  phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
82865
83988
  icon: "lightbulb-off",
82866
- args: { percentage: 0 }
83989
+ args: { level: 0 }
82867
83990
  },
82868
83991
  {
82869
83992
  id: "brightness.set",
82870
83993
  method: "setBrightness",
82871
83994
  label: t("Set brightness", "Imposta la luminosità"),
82872
- phrase: t("Set the brightness of {device} to {percentage}", "Imposta la luminosità di {device} a {percentage}"),
83995
+ phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
82873
83996
  icon: "sun-medium",
82874
83997
  openArgs: [{
82875
- key: "percentage",
83998
+ key: "level",
82876
83999
  label: PERCENT,
82877
84000
  offer: {
82878
84001
  kind: "number",
@@ -82886,13 +84009,13 @@ var ACTUATOR_CAP_VOCABULARY = {
82886
84009
  ],
82887
84010
  state: [{
82888
84011
  kind: "numeric",
82889
- field: "percentage",
84012
+ field: "level",
82890
84013
  quantity: t("the brightness", "la luminosità"),
82891
84014
  label: t("Brightness", "Luminosità"),
82892
84015
  unit: "%",
82893
84016
  icon: "sun"
82894
84017
  }],
82895
- events: { onBrightnessChanged: { notOperatorFacing: "the `percentage` state trigger reads the same change" } }
84018
+ events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
82896
84019
  },
82897
84020
  color: {
82898
84021
  scope: "device",
@@ -82906,7 +84029,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82906
84029
  icon: "sun",
82907
84030
  args: { color: {
82908
84031
  mode: "mired",
82909
- mireds: 370
84032
+ colorTemperature: 370
82910
84033
  } }
82911
84034
  },
82912
84035
  {
@@ -82917,7 +84040,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82917
84040
  icon: "snowflake",
82918
84041
  args: { color: {
82919
84042
  mode: "mired",
82920
- mireds: 200
84043
+ colorTemperature: 200
82921
84044
  } }
82922
84045
  },
82923
84046
  {
@@ -83531,7 +84654,7 @@ var ACTUATOR_CAP_VOCABULARY = {
83531
84654
  label: t("Fan off", "Ventilatore spento"),
83532
84655
  phrase: t("Stop {device}", "Ferma {device}"),
83533
84656
  icon: "power-off",
83534
- args: { percentage: 0 }
84657
+ args: { speed: 0 }
83535
84658
  },
83536
84659
  {
83537
84660
  id: "fan.full",
@@ -83539,16 +84662,16 @@ var ACTUATOR_CAP_VOCABULARY = {
83539
84662
  label: t("Fan full", "Ventilatore al massimo"),
83540
84663
  phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
83541
84664
  icon: "fan",
83542
- args: { percentage: 100 }
84665
+ args: { speed: 100 }
83543
84666
  },
83544
84667
  {
83545
84668
  id: "fan.speed",
83546
84669
  method: "setPercentage",
83547
84670
  label: t("Set speed", "Imposta la velocità"),
83548
- phrase: t("Set the speed of {device} to {percentage}", "Imposta la velocità di {device} al {percentage}"),
84671
+ phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
83549
84672
  icon: "fan",
83550
84673
  openArgs: [{
83551
- key: "percentage",
84674
+ key: "speed",
83552
84675
  label: t("Speed", "Velocità"),
83553
84676
  offer: {
83554
84677
  kind: "number",
@@ -83610,7 +84733,7 @@ var ACTUATOR_CAP_VOCABULARY = {
83610
84733
  ],
83611
84734
  state: [{
83612
84735
  kind: "numeric",
83613
- field: "percentage",
84736
+ field: "speed",
83614
84737
  quantity: t("the speed", "la velocità"),
83615
84738
  label: t("Speed", "Velocità"),
83616
84739
  unit: "%",
@@ -84408,7 +85531,7 @@ var APPLIANCE_CAP_VOCABULARY = {
84408
85531
  },
84409
85532
  {
84410
85533
  kind: "numeric",
84411
- field: "progressPercent",
85534
+ field: "progress",
84412
85535
  quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
84413
85536
  label: t("Progress", "Avanzamento"),
84414
85537
  unit: "%",
@@ -84766,10 +85889,10 @@ var APPLIANCE_CAP_VOCABULARY = {
84766
85889
  id: "media.seek",
84767
85890
  method: "seek",
84768
85891
  label: t("Seek", "Vai al punto"),
84769
- phrase: t("Move {device} to {positionMs}", "Porta {device} al punto {positionMs}"),
85892
+ phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
84770
85893
  icon: "fast-forward",
84771
85894
  openArgs: [{
84772
- key: "positionMs",
85895
+ key: "position",
84773
85896
  label: t("Position", "Posizione"),
84774
85897
  offer: {
84775
85898
  kind: "number",
@@ -85221,7 +86344,39 @@ var APPLIANCE_CAP_VOCABULARY = {
85221
86344
  phrase: t("When {device} finishes", "Quando {device} finisce"),
85222
86345
  icon: "square"
85223
86346
  }
85224
- }
86347
+ },
86348
+ overrides: { byType: {
86349
+ [DeviceType.Washer]: {
86350
+ state: { running: {
86351
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
86352
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
86353
+ } },
86354
+ events: {
86355
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
86356
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
86357
+ }
86358
+ },
86359
+ [DeviceType.Dryer]: {
86360
+ state: { running: {
86361
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
86362
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
86363
+ } },
86364
+ events: {
86365
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
86366
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
86367
+ }
86368
+ },
86369
+ [DeviceType.Dishwasher]: {
86370
+ state: { running: {
86371
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
86372
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
86373
+ } },
86374
+ events: {
86375
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
86376
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
86377
+ }
86378
+ }
86379
+ } }
85225
86380
  },
85226
86381
  update: {
85227
86382
  scope: "device",
@@ -85786,7 +86941,7 @@ var SENSOR_CAP_VOCABULARY = {
85786
86941
  state: [
85787
86942
  {
85788
86943
  kind: "numeric",
85789
- field: "co2Ppm",
86944
+ field: "co2",
85790
86945
  quantity: t("the CO₂", "la CO₂"),
85791
86946
  label: t("CO₂", "CO₂"),
85792
86947
  unit: "ppm",
@@ -85794,7 +86949,7 @@ var SENSOR_CAP_VOCABULARY = {
85794
86949
  },
85795
86950
  {
85796
86951
  kind: "numeric",
85797
- field: "vocPpb",
86952
+ field: "voc",
85798
86953
  quantity: t("the VOC level", "il livello di COV"),
85799
86954
  label: t("VOC", "COV"),
85800
86955
  unit: "ppb",
@@ -85830,7 +86985,7 @@ var SENSOR_CAP_VOCABULARY = {
85830
86985
  noun: t("Light level", "Luminosità ambiente"),
85831
86986
  state: [{
85832
86987
  kind: "numeric",
85833
- field: "lux",
86988
+ field: "illuminance",
85834
86989
  quantity: t("the light level", "la luminosità"),
85835
86990
  label: t("Illuminance", "Illuminamento"),
85836
86991
  unit: "lx",
@@ -85993,6 +87148,10 @@ var SENSOR_CAP_VOCABULARY = {
85993
87148
  icon: "flame"
85994
87149
  }]
85995
87150
  },
87151
+ "group-active": {
87152
+ scope: "device",
87153
+ noun: t("Group state", "Stato del gruppo")
87154
+ },
85996
87155
  "group-commands": {
85997
87156
  scope: "device",
85998
87157
  noun: t("Group command", "Comando di gruppo")
@@ -86029,7 +87188,7 @@ var SENSOR_CAP_VOCABULARY = {
86029
87188
  noun: t("Humidity", "Umidità"),
86030
87189
  state: [{
86031
87190
  kind: "numeric",
86032
- field: "percent",
87191
+ field: "humidity",
86033
87192
  quantity: t("the humidity", "l’umidità"),
86034
87193
  label: t("Humidity", "Umidità"),
86035
87194
  unit: "%",
@@ -86049,6 +87208,17 @@ var SENSOR_CAP_VOCABULARY = {
86049
87208
  }],
86050
87209
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
86051
87210
  },
87211
+ meter: {
87212
+ scope: "device",
87213
+ noun: t("Meter", "Contatore"),
87214
+ state: [{
87215
+ kind: "numeric",
87216
+ field: "value",
87217
+ quantity: t("the total", "il totale"),
87218
+ label: t("Total", "Totale"),
87219
+ icon: "gauge"
87220
+ }]
87221
+ },
86052
87222
  "numeric-sensor": {
86053
87223
  scope: "device",
86054
87224
  noun: t("Sensor", "Sensore"),
@@ -86066,7 +87236,7 @@ var SENSOR_CAP_VOCABULARY = {
86066
87236
  state: [
86067
87237
  {
86068
87238
  kind: "numeric",
86069
- field: "watts",
87239
+ field: "power",
86070
87240
  quantity: t("the power draw", "il consumo"),
86071
87241
  label: t("Power", "Potenza"),
86072
87242
  unit: "W",
@@ -86074,7 +87244,7 @@ var SENSOR_CAP_VOCABULARY = {
86074
87244
  },
86075
87245
  {
86076
87246
  kind: "numeric",
86077
- field: "kwhTotal",
87247
+ field: "energy",
86078
87248
  quantity: t("the energy used", "l’energia consumata"),
86079
87249
  label: t("Energy", "Energia"),
86080
87250
  unit: "kWh",
@@ -86082,7 +87252,7 @@ var SENSOR_CAP_VOCABULARY = {
86082
87252
  },
86083
87253
  {
86084
87254
  kind: "numeric",
86085
- field: "volts",
87255
+ field: "voltage",
86086
87256
  quantity: t("the voltage", "la tensione"),
86087
87257
  label: t("Voltage", "Tensione"),
86088
87258
  unit: "V",
@@ -86090,7 +87260,7 @@ var SENSOR_CAP_VOCABULARY = {
86090
87260
  },
86091
87261
  {
86092
87262
  kind: "numeric",
86093
- field: "amps",
87263
+ field: "current",
86094
87264
  quantity: t("the current", "la corrente"),
86095
87265
  label: t("Current", "Corrente"),
86096
87266
  unit: "A",
@@ -86107,13 +87277,6 @@ var SENSOR_CAP_VOCABULARY = {
86107
87277
  label: t("Where", "Posizione"),
86108
87278
  phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
86109
87279
  icon: "map-pin"
86110
- }, {
86111
- kind: "numeric",
86112
- field: "batteryPercent",
86113
- quantity: t("the phone battery", "la batteria del telefono"),
86114
- label: t("Battery", "Batteria"),
86115
- unit: "%",
86116
- icon: "battery"
86117
87280
  }]
86118
87281
  },
86119
87282
  "pressure-sensor": {
@@ -86121,7 +87284,7 @@ var SENSOR_CAP_VOCABULARY = {
86121
87284
  noun: t("Pressure", "Pressione"),
86122
87285
  state: [{
86123
87286
  kind: "numeric",
86124
- field: "hpa",
87287
+ field: "pressure",
86125
87288
  quantity: t("the pressure", "la pressione"),
86126
87289
  label: t("Pressure", "Pressione"),
86127
87290
  unit: "hPa",
@@ -86157,7 +87320,7 @@ var SENSOR_CAP_VOCABULARY = {
86157
87320
  noun: t("Temperature", "Temperatura"),
86158
87321
  state: [{
86159
87322
  kind: "numeric",
86160
- field: "celsius",
87323
+ field: "temperature",
86161
87324
  quantity: t("the temperature", "la temperatura"),
86162
87325
  label: t("Temperature", "Temperatura"),
86163
87326
  unit: "°C",
@@ -86228,7 +87391,7 @@ var SENSOR_CAP_VOCABULARY = {
86228
87391
  state: [
86229
87392
  {
86230
87393
  kind: "numeric",
86231
- field: "percentage",
87394
+ field: "level",
86232
87395
  quantity: t("the battery level", "il livello della batteria"),
86233
87396
  label: t("Battery", "Batteria"),
86234
87397
  unit: "%",
@@ -86270,7 +87433,7 @@ var SENSOR_CAP_VOCABULARY = {
86270
87433
  icon: "battery-charging"
86271
87434
  }
86272
87435
  ],
86273
- events: { onStatusChanged: { notOperatorFacing: "the percentage / sleeping / charging state triggers each read one field of this change" } },
87436
+ events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
86274
87437
  extraEvents: [{
86275
87438
  category: EventCategory.DeviceAwake,
86276
87439
  label: t("Woke up", "Risvegliato"),
@@ -86288,7 +87451,7 @@ var SENSOR_CAP_VOCABULARY = {
86288
87451
  noun: t("Network link", "Collegamento di rete"),
86289
87452
  state: [{
86290
87453
  kind: "numeric",
86291
- field: "signalPercent",
87454
+ field: "signal",
86292
87455
  quantity: t("the signal strength", "l’intensità del segnale"),
86293
87456
  label: t("Signal", "Segnale"),
86294
87457
  unit: "%",
@@ -86381,6 +87544,8 @@ var ML = "model and training machinery — no device action";
86381
87544
  var INTERNAL = "internal plumbing between addons — never operator-facing";
86382
87545
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
86383
87546
  var SYSTEM_CAP_VOCABULARY = {
87547
+ "action-source": system(INTERNAL),
87548
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
86384
87549
  "addon-pages": system(UI_PLUMBING),
86385
87550
  "addon-pages-source": system(UI_PLUMBING),
86386
87551
  "addon-routes": system(UI_PLUMBING),
@@ -86521,8 +87686,47 @@ function projectAction(cap, action) {
86521
87686
  };
86522
87687
  }
86523
87688
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
86524
- 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;
87689
+ 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;
86525
87690
  DeviceFeature.BatteryOperated;
87691
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
87692
+ 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.";
87693
+ 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.";
87694
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
87695
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
87696
+ 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.";
87697
+ function withheld(reason) {
87698
+ return {
87699
+ [DeviceType.Washer]: {
87700
+ exported: false,
87701
+ reason
87702
+ },
87703
+ [DeviceType.Dryer]: {
87704
+ exported: false,
87705
+ reason
87706
+ },
87707
+ [DeviceType.Dishwasher]: {
87708
+ exported: false,
87709
+ reason
87710
+ }
87711
+ };
87712
+ }
87713
+ function offered(received) {
87714
+ return {
87715
+ [DeviceType.Washer]: {
87716
+ exported: true,
87717
+ as: received
87718
+ },
87719
+ [DeviceType.Dryer]: {
87720
+ exported: true,
87721
+ as: received
87722
+ },
87723
+ [DeviceType.Dishwasher]: {
87724
+ exported: true,
87725
+ as: received
87726
+ }
87727
+ };
87728
+ }
87729
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
86526
87730
  var LAST_FETCHED_FIELD = "lastFetchedAt";
86527
87731
  /**
86528
87732
  * How long a bridge stops re-attempting a refresh that did not land.
@@ -86690,6 +87894,31 @@ Object.freeze({
86690
87894
  form: "single",
86691
87895
  optional: false
86692
87896
  }],
87897
+ "actions.get": [{
87898
+ name: "deviceId",
87899
+ form: "single",
87900
+ optional: true
87901
+ }],
87902
+ "actions.getLinks": [{
87903
+ name: "deviceId",
87904
+ form: "single",
87905
+ optional: false
87906
+ }],
87907
+ "actions.listForCamera": [{
87908
+ name: "deviceId",
87909
+ form: "single",
87910
+ optional: false
87911
+ }],
87912
+ "actions.run": [{
87913
+ name: "deviceId",
87914
+ form: "single",
87915
+ optional: true
87916
+ }],
87917
+ "actions.setLinks": [{
87918
+ name: "deviceId",
87919
+ form: "single",
87920
+ optional: false
87921
+ }],
86693
87922
  "addonSettings.getDeviceSettings": [{
86694
87923
  name: "deviceId",
86695
87924
  form: "single",
@@ -87641,6 +88870,11 @@ Object.freeze({
87641
88870
  form: "single",
87642
88871
  optional: false
87643
88872
  }],
88873
+ "groupActive.getStatus": [{
88874
+ name: "deviceId",
88875
+ form: "single",
88876
+ optional: false
88877
+ }],
87644
88878
  "groupCommands.getStatus": [{
87645
88879
  name: "deviceId",
87646
88880
  form: "single",
@@ -87851,6 +89085,11 @@ Object.freeze({
87851
89085
  form: "single",
87852
89086
  optional: false
87853
89087
  }],
89088
+ "meter.getStatus": [{
89089
+ name: "deviceId",
89090
+ form: "single",
89091
+ optional: false
89092
+ }],
87854
89093
  "motion.getStatus": [{
87855
89094
  name: "deviceId",
87856
89095
  form: "single",
@@ -89458,7 +90697,6 @@ Object.freeze({
89458
90697
  "network-access": "ingress",
89459
90698
  "smtp-provider": "email"
89460
90699
  });
89461
- DeviceType.Camera;
89462
90700
  /**
89463
90701
  * A saved Customize assignment (D936). The schema is the type: callers use
89464
90702
  * `z.infer`, they do not restate the shape.
@@ -90813,6 +92051,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
90813
92051
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
90814
92052
  DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
90815
92053
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
92054
+ String.raw`(?<=^|["'(\s>,\[{])`;
90816
92055
  var MB = 1024 * 1024;
90817
92056
  1024 * MB, 3072 * MB;
90818
92057
  //#endregion
@@ -200182,7 +201421,7 @@ var FloodlightAccessory = class extends BaseDevice {
200182
201421
  lastChangedAt: ts
200183
201422
  });
200184
201423
  if (brightness !== void 0) this.setCapSlice(brightnessCapability, {
200185
- percentage: brightness,
201424
+ level: brightness,
200186
201425
  lastChangedAt: ts
200187
201426
  });
200188
201427
  this.hasFetchedFromFirmware = true;
@@ -200301,7 +201540,7 @@ var FloodlightAccessory = class extends BaseDevice {
200301
201540
  }
200302
201541
  brightnessView() {
200303
201542
  return this.getCapSlice(brightnessCapability) ?? {
200304
- percentage: 100,
201543
+ level: 100,
200305
201544
  lastChangedAt: 0
200306
201545
  };
200307
201546
  }
@@ -200326,19 +201565,19 @@ var FloodlightAccessory = class extends BaseDevice {
200326
201565
  registerBrightnessCap() {
200327
201566
  this.ctx.registerNativeCap(brightnessCapability, {
200328
201567
  getStatus: async () => this.brightnessView(),
200329
- setBrightness: async ({ deviceId, percentage }) => {
201568
+ setBrightness: async ({ deviceId, level: percentage }) => {
200330
201569
  if (deviceId !== this.id) return;
200331
201570
  const value = Math.max(0, Math.min(100, percentage));
200332
201571
  if (this.switchView().on) await (await this.api).setWhiteLedState(this.channel, void 0, value);
200333
201572
  this.setCapSlice(brightnessCapability, {
200334
- percentage: value,
201573
+ level: value,
200335
201574
  lastChangedAt: Date.now()
200336
201575
  });
200337
201576
  this.lastSetAtMs = Date.now();
200338
201577
  }
200339
201578
  });
200340
201579
  this.setCapSlice(brightnessCapability, {
200341
- percentage: 100,
201580
+ level: 100,
200342
201581
  lastChangedAt: 0
200343
201582
  });
200344
201583
  }
@@ -201014,13 +202253,13 @@ function mapNetworkReadout(readout, now) {
201014
202253
  const type = linkTypeOf(readout);
201015
202254
  const raw = readout.wifiSignal;
201016
202255
  const wireless = type === "wifi" || type === "cellular";
201017
- const signalPercent = wireless ? signalPercentOf(raw) : null;
202256
+ const signal = wireless ? signalPercentOf(raw) : null;
201018
202257
  const rssiDbm = wireless && raw !== void 0 && Number.isFinite(raw) && raw < 0 ? raw : void 0;
201019
202258
  const ssid = wireless && readout.ssid !== void 0 && readout.ssid !== "" ? readout.ssid : void 0;
201020
202259
  return {
201021
202260
  type,
201022
- signalPercent,
201023
- ...rssiDbm !== void 0 ? { rssiDbm } : {},
202261
+ signal,
202262
+ ...rssiDbm !== void 0 ? { rssi: rssiDbm } : {},
201024
202263
  ...ssid !== void 0 ? { ssid } : {},
201025
202264
  lastUpdated: now
201026
202265
  };
@@ -204263,7 +205502,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
204263
205502
  */
204264
205503
  mapBatteryInfo(info) {
204265
205504
  return {
204266
- percentage: typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.percentage ?? null,
205505
+ level: typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.level ?? null,
204267
205506
  charging: reportsPowerSource(info) ? deriveChargingSource(info.adapterStatus, info.chargeStatus) : this.state.battery.charging ?? "unknown",
204268
205507
  sleeping: info.sleeping === true || this.sleeping,
204269
205508
  lastUpdated: Date.now()
@@ -204399,7 +205638,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
204399
205638
  }
204400
205639
  });
204401
205640
  if (this.getCapSlice(batteryCapability) === null) this.setCapSlice(batteryCapability, {
204402
- percentage: null,
205641
+ level: null,
204403
205642
  charging: "none",
204404
205643
  sleeping: false,
204405
205644
  lastUpdated: Date.now()
@@ -204627,7 +205866,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
204627
205866
  });
204628
205867
  this.setCapSlice(batteryCapability, {
204629
205868
  ...this.state.battery,
204630
- percentage: mapped.percentage,
205869
+ level: mapped.level,
204631
205870
  charging: mapped.charging,
204632
205871
  lastUpdated: Date.now()
204633
205872
  });