@camstack/addon-matter-broker 0.2.150 → 0.2.152

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
@@ -13,7 +13,7 @@ let node_stream_promises = require("node:stream/promises");
13
13
  let node_net = require("node:net");
14
14
  let node_dgram = require("node:dgram");
15
15
  node_dgram = require_esm.__toESM(node_dgram, 1);
16
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
16
+ //#region ../types/dist/event-category-DmZDPDf_.mjs
17
17
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
18
18
  EventCategory["SystemBoot"] = "system.boot";
19
19
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -640,7 +640,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
640
640
  * (a track was promoted to a stationary-registry entry) or departs (the
641
641
  * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
642
642
  * the dedicated "Stationary" section — never the live event feed. Payload:
643
- * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
643
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
644
644
  */
645
645
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
646
646
  /**
@@ -778,6 +778,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
778
778
  */
779
779
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
780
780
  /**
781
+ * An action run started or ended. Emitted by the `actions` surface.
782
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
783
+ */
784
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
785
+ /**
781
786
  * Progress update from a running model conversion job.
782
787
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
783
788
  * Emitted by `addon-model-studio` on the converting node.
@@ -5589,7 +5594,7 @@ var ZodIssueCode = {
5589
5594
  var ZodFirstPartyTypeKind;
5590
5595
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5591
5596
  //#endregion
5592
- //#region ../types/dist/sleep-bykKrTgu.mjs
5597
+ //#region ../types/dist/sleep-Crlmu_tO.mjs
5593
5598
  /**
5594
5599
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5595
5600
  * window to float samples (D455).
@@ -8897,6 +8902,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
8897
8902
  /** Every capability name, so addon code can check a name without the schema graph. */
8898
8903
  var CAP_NAMES = Object.freeze([
8899
8904
  "accessories",
8905
+ "action-source",
8906
+ "actions",
8900
8907
  "addon-pages",
8901
8908
  "addon-pages-source",
8902
8909
  "addon-routes",
@@ -8970,6 +8977,7 @@ var CAP_NAMES = Object.freeze([
8970
8977
  "filesystem-browse",
8971
8978
  "flood",
8972
8979
  "gas",
8980
+ "group-active",
8973
8981
  "group-commands",
8974
8982
  "group-status",
8975
8983
  "heating-valve",
@@ -8992,6 +9000,7 @@ var CAP_NAMES = Object.freeze([
8992
9000
  "login-method",
8993
9001
  "media-player",
8994
9002
  "mesh-network",
9003
+ "meter",
8995
9004
  "metrics-provider",
8996
9005
  "model-convert",
8997
9006
  "model-distributor",
@@ -9083,11 +9092,11 @@ var CAP_NAMES = Object.freeze([
9083
9092
  Object.freeze({
9084
9093
  "air-quality-sensor": [
9085
9094
  {
9086
- "path": "co2Ppm",
9095
+ "path": "co2",
9087
9096
  "valueType": "number"
9088
9097
  },
9089
9098
  {
9090
- "path": "vocPpb",
9099
+ "path": "voc",
9091
9100
  "valueType": "number"
9092
9101
  },
9093
9102
  {
@@ -9137,7 +9146,7 @@ Object.freeze({
9137
9146
  }
9138
9147
  ],
9139
9148
  "ambient-light-sensor": [{
9140
- "path": "lux",
9149
+ "path": "illuminance",
9141
9150
  "valueType": "number"
9142
9151
  }],
9143
9152
  "audio-metrics": [
@@ -9198,7 +9207,7 @@ Object.freeze({
9198
9207
  ],
9199
9208
  "battery": [
9200
9209
  {
9201
- "path": "percentage",
9210
+ "path": "level",
9202
9211
  "valueType": "number"
9203
9212
  },
9204
9213
  {
@@ -9220,7 +9229,7 @@ Object.freeze({
9220
9229
  "valueType": "number"
9221
9230
  },
9222
9231
  {
9223
- "path": "volts",
9232
+ "path": "voltage",
9224
9233
  "valueType": "number"
9225
9234
  },
9226
9235
  {
@@ -9241,7 +9250,7 @@ Object.freeze({
9241
9250
  "valueType": "boolean"
9242
9251
  }],
9243
9252
  "brightness": [{
9244
- "path": "percentage",
9253
+ "path": "level",
9245
9254
  "valueType": "number"
9246
9255
  }],
9247
9256
  "broker": [{
@@ -9442,7 +9451,7 @@ Object.freeze({
9442
9451
  "valueType": "number"
9443
9452
  },
9444
9453
  {
9445
- "path": "mireds",
9454
+ "path": "colorTemperature",
9446
9455
  "valueType": "number"
9447
9456
  }
9448
9457
  ],
@@ -9621,15 +9630,15 @@ Object.freeze({
9621
9630
  "valueType": "string"
9622
9631
  },
9623
9632
  {
9624
- "path": "remainingSeconds",
9633
+ "path": "remaining",
9625
9634
  "valueType": "number"
9626
9635
  },
9627
9636
  {
9628
- "path": "delayedStartSeconds",
9637
+ "path": "delayedStart",
9629
9638
  "valueType": "number"
9630
9639
  },
9631
9640
  {
9632
- "path": "progressPercent",
9641
+ "path": "progress",
9633
9642
  "valueType": "number"
9634
9643
  },
9635
9644
  {
@@ -9743,7 +9752,7 @@ Object.freeze({
9743
9752
  ],
9744
9753
  "fan-control": [
9745
9754
  {
9746
- "path": "percentage",
9755
+ "path": "speed",
9747
9756
  "valueType": "number"
9748
9757
  },
9749
9758
  {
@@ -9790,6 +9799,10 @@ Object.freeze({
9790
9799
  "path": "detected",
9791
9800
  "valueType": "boolean"
9792
9801
  }],
9802
+ "group-active": [{
9803
+ "path": "active",
9804
+ "valueType": "boolean"
9805
+ }],
9793
9806
  "group-status": [
9794
9807
  {
9795
9808
  "path": "included",
@@ -9839,7 +9852,7 @@ Object.freeze({
9839
9852
  }
9840
9853
  ],
9841
9854
  "humidity-sensor": [{
9842
- "path": "percent",
9855
+ "path": "humidity",
9843
9856
  "valueType": "number"
9844
9857
  }],
9845
9858
  "image": [{
@@ -9949,7 +9962,7 @@ Object.freeze({
9949
9962
  "valueType": "number"
9950
9963
  },
9951
9964
  {
9952
- "path": "progressPercent",
9965
+ "path": "progress",
9953
9966
  "valueType": "number"
9954
9967
  },
9955
9968
  {
@@ -10028,11 +10041,11 @@ Object.freeze({
10028
10041
  "valueType": "string"
10029
10042
  },
10030
10043
  {
10031
- "path": "positionMs",
10044
+ "path": "position",
10032
10045
  "valueType": "number"
10033
10046
  },
10034
10047
  {
10035
- "path": "durationMs",
10048
+ "path": "duration",
10036
10049
  "valueType": "number"
10037
10050
  },
10038
10051
  {
@@ -10049,6 +10062,10 @@ Object.freeze({
10049
10062
  ]
10050
10063
  }
10051
10064
  ],
10065
+ "meter": [{
10066
+ "path": "value",
10067
+ "valueType": "number"
10068
+ }],
10052
10069
  "motion": [
10053
10070
  {
10054
10071
  "path": "detected",
@@ -10130,11 +10147,11 @@ Object.freeze({
10130
10147
  ]
10131
10148
  },
10132
10149
  {
10133
- "path": "signalPercent",
10150
+ "path": "signal",
10134
10151
  "valueType": "number"
10135
10152
  },
10136
10153
  {
10137
- "path": "rssiDbm",
10154
+ "path": "rssi",
10138
10155
  "valueType": "number"
10139
10156
  },
10140
10157
  {
@@ -10189,6 +10206,18 @@ Object.freeze({
10189
10206
  "path": "foodLevel",
10190
10207
  "valueType": "number"
10191
10208
  },
10209
+ {
10210
+ "path": "bowlFood",
10211
+ "valueType": "number"
10212
+ },
10213
+ {
10214
+ "path": "eating",
10215
+ "valueType": "boolean"
10216
+ },
10217
+ {
10218
+ "path": "supportsMarkRefilled",
10219
+ "valueType": "boolean"
10220
+ },
10192
10221
  {
10193
10222
  "path": "food1",
10194
10223
  "valueType": "number"
@@ -10253,23 +10282,23 @@ Object.freeze({
10253
10282
  ],
10254
10283
  "power-meter": [
10255
10284
  {
10256
- "path": "watts",
10285
+ "path": "power",
10257
10286
  "valueType": "number"
10258
10287
  },
10259
10288
  {
10260
- "path": "kwhTotal",
10289
+ "path": "energy",
10261
10290
  "valueType": "number"
10262
10291
  },
10263
10292
  {
10264
- "path": "volts",
10293
+ "path": "voltage",
10265
10294
  "valueType": "number"
10266
10295
  },
10267
10296
  {
10268
- "path": "amps",
10297
+ "path": "current",
10269
10298
  "valueType": "number"
10270
10299
  },
10271
10300
  {
10272
- "path": "va",
10301
+ "path": "apparentPower",
10273
10302
  "valueType": "number"
10274
10303
  },
10275
10304
  {
@@ -10297,14 +10326,10 @@ Object.freeze({
10297
10326
  {
10298
10327
  "path": "gps.accuracyMeters",
10299
10328
  "valueType": "number"
10300
- },
10301
- {
10302
- "path": "batteryPercent",
10303
- "valueType": "number"
10304
10329
  }
10305
10330
  ],
10306
10331
  "pressure-sensor": [{
10307
- "path": "hpa",
10332
+ "path": "pressure",
10308
10333
  "valueType": "number"
10309
10334
  }],
10310
10335
  "privacy-mask": [{
@@ -10618,7 +10643,7 @@ Object.freeze({
10618
10643
  "valueType": "boolean"
10619
10644
  }],
10620
10645
  "temperature-sensor": [{
10621
- "path": "celsius",
10646
+ "path": "temperature",
10622
10647
  "valueType": "number"
10623
10648
  }],
10624
10649
  "update": [
@@ -10666,7 +10691,7 @@ Object.freeze({
10666
10691
  "valueType": "string"
10667
10692
  },
10668
10693
  {
10669
- "path": "progressPercent",
10694
+ "path": "progress",
10670
10695
  "valueType": "number"
10671
10696
  },
10672
10697
  {
@@ -10792,7 +10817,7 @@ object({
10792
10817
  params: record(string$2(), union([string$2(), number()])).optional()
10793
10818
  });
10794
10819
  //#endregion
10795
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10820
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10796
10821
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10797
10822
  DeviceType["Camera"] = "camera";
10798
10823
  DeviceType["Hub"] = "hub";
@@ -10886,8 +10911,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10886
10911
  DeviceType["PetFeeder"] = "pet-feeder";
10887
10912
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10888
10913
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10889
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10914
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10915
+ * A plug-built dishwasher carries running-state and power instead. */
10890
10916
  DeviceType["Dishwasher"] = "dishwasher";
10917
+ /** Washing machine. A plug-built one is running while its power stays
10918
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10919
+ DeviceType["Washer"] = "washer";
10920
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10921
+ DeviceType["Dryer"] = "dryer";
10891
10922
  return DeviceType;
10892
10923
  }({});
10893
10924
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11145,7 +11176,7 @@ function errMsg(err) {
11145
11176
  return String(err);
11146
11177
  }
11147
11178
  //#endregion
11148
- //#region ../types/dist/composition-OhpuespA.mjs
11179
+ //#region ../types/dist/composition-CZImLpOI.mjs
11149
11180
  /**
11150
11181
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11151
11182
  *
@@ -11460,6 +11491,13 @@ var table = {
11460
11491
  apply: () => {
11461
11492
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11462
11493
  }
11494
+ },
11495
+ schmitt: {
11496
+ minArgs: 3,
11497
+ maxArgs: 5,
11498
+ apply: () => {
11499
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11500
+ }
11463
11501
  }
11464
11502
  };
11465
11503
  Object.freeze(Object.assign(Object.create(null), table));
@@ -11901,6 +11939,99 @@ function validateExpressionSource(src) {
11901
11939
  return null;
11902
11940
  }
11903
11941
  /**
11942
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11943
+ *
11944
+ * Standby of the control board is under 5 W; a motor or a heater is far
11945
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11946
+ * a cycle, so running starts only after 30 s above the line.
11947
+ *
11948
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11949
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11950
+ * report "finished" in the middle of the program, so the stop dwell is
11951
+ * 10 minutes on every type. The number is stored per type so one machine
11952
+ * can move later without moving the others.
11953
+ */
11954
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11955
+ var APPLIANCE_KINDS = [
11956
+ "washer",
11957
+ "dryer",
11958
+ "dishwasher"
11959
+ ];
11960
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11961
+ /**
11962
+ * Statistics and integrals over a history read (D944).
11963
+ *
11964
+ * Pure: the same points always give the same answer, so a restart recomputes
11965
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11966
+ * never the mean of the samples. A hole with no held value is not integrated
11967
+ * as zero. A truncated read is not published.
11968
+ */
11969
+ var STATISTIC_STATS = [
11970
+ "min",
11971
+ "max",
11972
+ "mean",
11973
+ "sum",
11974
+ "count",
11975
+ "change"
11976
+ ];
11977
+ /**
11978
+ * The range a statistics or integral helper asks history for (D944).
11979
+ *
11980
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11981
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11982
+ * is strictly longer than the longest that range can be. Forever qualifies.
11983
+ * Equal to a tier's retention is a refusal, not a short answer.
11984
+ */
11985
+ var MINUTE_MS = 6e4;
11986
+ var HOUR_MS = 60 * MINUTE_MS;
11987
+ var DAY_MS = 24 * HOUR_MS;
11988
+ var ROLLING_SPANS = [
11989
+ "1h",
11990
+ "24h",
11991
+ "2d",
11992
+ "7d",
11993
+ "30d",
11994
+ "custom"
11995
+ ];
11996
+ var CALENDAR_PERIODS = [
11997
+ "today",
11998
+ "yesterday",
11999
+ "this-week",
12000
+ "this-month"
12001
+ ];
12002
+ var RESET_PERIODS = [
12003
+ "never",
12004
+ "daily",
12005
+ "weekly",
12006
+ "monthly"
12007
+ ];
12008
+ 5 * MINUTE_MS;
12009
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
12010
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
12011
+ var rollingWindowObject = object({
12012
+ kind: literal("rolling"),
12013
+ span: _enum(ROLLING_SPANS),
12014
+ customMinutes: number().int().min(5).optional()
12015
+ }).check((payload) => {
12016
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
12017
+ code: "custom",
12018
+ message: "a custom range needs a duration in minutes",
12019
+ path: ["customMinutes"],
12020
+ input: payload.value
12021
+ });
12022
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
12023
+ code: "custom",
12024
+ message: "only a custom range carries a duration",
12025
+ path: ["customMinutes"],
12026
+ input: payload.value
12027
+ });
12028
+ });
12029
+ var RollingWindowSchema = rollingWindowObject;
12030
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
12031
+ kind: literal("calendar"),
12032
+ period: _enum(CALENDAR_PERIODS)
12033
+ })]);
12034
+ /**
11904
12035
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11905
12036
  * id. Split out so helper schemas can name it without a cycle through
11906
12037
  * `composition.ts` (D659).
@@ -11926,7 +12057,13 @@ var GROUP_TEMPLATE_IDS = [
11926
12057
  "occupancy",
11927
12058
  "binary",
11928
12059
  "switch",
11929
- "light"
12060
+ "light",
12061
+ "fan",
12062
+ "cover",
12063
+ "valve",
12064
+ "lock",
12065
+ "media-player",
12066
+ "climate"
11930
12067
  ];
11931
12068
  var NUMERIC_AGGREGATES = [
11932
12069
  "mean",
@@ -11956,14 +12093,73 @@ var HelperMemberSchema = object({
11956
12093
  cap: string$2().min(1),
11957
12094
  fieldPath: string$2().min(1)
11958
12095
  });
11959
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11960
- kind: literal("group"),
11961
- templateId: GroupTemplateIdSchema,
11962
- aggregate: AggregateReducerSchema,
11963
- members: array(HelperMemberSchema).min(1).max(64),
11964
- /** Actuator groups only. Readings leave this unset. */
11965
- active: ActiveCriterionSchema.optional()
11966
- })]);
12096
+ var HelperSpecSchema = discriminatedUnion("kind", [
12097
+ object({
12098
+ kind: literal("group"),
12099
+ templateId: GroupTemplateIdSchema,
12100
+ aggregate: AggregateReducerSchema,
12101
+ members: array(HelperMemberSchema).min(1).max(64),
12102
+ /** Actuator groups only. Readings leave this unset. */
12103
+ active: ActiveCriterionSchema.optional()
12104
+ }),
12105
+ object({
12106
+ kind: literal("appliance"),
12107
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
12108
+ appliance: _enum(APPLIANCE_KINDS),
12109
+ thresholdWatts: number().positive(),
12110
+ startDwellMs: number().int().nonnegative(),
12111
+ stopDwellMs: number().int().nonnegative(),
12112
+ member: HelperMemberSchema
12113
+ }),
12114
+ object({
12115
+ kind: literal("statistic"),
12116
+ stat: _enum(STATISTIC_STATS),
12117
+ window: StatisticWindowSchema,
12118
+ source: HelperMemberSchema
12119
+ }),
12120
+ object({
12121
+ kind: literal("integral"),
12122
+ reset: _enum(RESET_PERIODS),
12123
+ output: _enum(["energy", "meter"]),
12124
+ source: HelperMemberSchema
12125
+ }),
12126
+ object({
12127
+ kind: literal("threshold"),
12128
+ source: HelperMemberSchema,
12129
+ high: number(),
12130
+ low: number(),
12131
+ onForMs: number().int().nonnegative(),
12132
+ offForMs: number().int().nonnegative()
12133
+ }),
12134
+ object({
12135
+ kind: literal("arithmetic"),
12136
+ op: _enum([
12137
+ "sum",
12138
+ "difference",
12139
+ "ratio"
12140
+ ]),
12141
+ inputs: array(HelperMemberSchema).min(2).max(64)
12142
+ }),
12143
+ object({
12144
+ kind: literal("expression"),
12145
+ expr: string$2().min(1),
12146
+ inputs: array(object({
12147
+ name: string$2().min(1),
12148
+ member: HelperMemberSchema
12149
+ })).min(1).max(64),
12150
+ output: _enum(["number", "boolean"])
12151
+ }),
12152
+ object({
12153
+ kind: literal("derivative"),
12154
+ window: RollingWindowSchema,
12155
+ per: _enum([
12156
+ "second",
12157
+ "minute",
12158
+ "hour"
12159
+ ]),
12160
+ source: HelperMemberSchema
12161
+ })
12162
+ ]);
11967
12163
  /** A composed device's stableId is this prefix plus the block id. */
11968
12164
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11969
12165
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -12024,6 +12220,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12024
12220
  "all"
12025
12221
  ]),
12026
12222
  reads: array(CompositionFieldReadSchema).min(1).max(64),
12223
+ /**
12224
+ * Set when the reads are an enum state, not a yes/no: the listed states
12225
+ * count as active (a cover is `open`). A state outside the list is inactive;
12226
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
12227
+ */
12228
+ activeWhen: array(string$2().min(1)).min(1).optional(),
12027
12229
  /** `value` is the reading; the others are the member counts. */
12028
12230
  publish: _enum([
12029
12231
  "value",
@@ -12037,6 +12239,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12037
12239
  "omit",
12038
12240
  "offline"
12039
12241
  ])
12242
+ }),
12243
+ object({
12244
+ kind: literal("series"),
12245
+ read: CompositionFieldReadSchema,
12246
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12247
+ publish: _enum([
12248
+ "value",
12249
+ "included",
12250
+ "excluded",
12251
+ "total"
12252
+ ]),
12253
+ /**
12254
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12255
+ * device unseeded until history answers, so a restart cannot publish 0.
12256
+ */
12257
+ empty: _enum([
12258
+ "null",
12259
+ "omit",
12260
+ "offline"
12261
+ ])
12040
12262
  })
12041
12263
  ]);
12042
12264
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -15474,6 +15696,14 @@ var AutomationConditionLeafSchema = object({
15474
15696
  array(union([string$2(), number()]))
15475
15697
  ])
15476
15698
  });
15699
+ var AutomationConditionStationaryPresentSchema = object({
15700
+ kind: literal("stationary-present"),
15701
+ deviceId: number().int().nonnegative(),
15702
+ /** Class names; a macro class matches every raw class that maps to it. */
15703
+ classes: array(string$2().min(1)).min(1).optional(),
15704
+ /** The parked object must be in at least one of these zones. */
15705
+ zones: array(string$2().min(1)).min(1).optional()
15706
+ });
15477
15707
  /**
15478
15708
  * The expression leaf, declared as a plain object rather than an intersection
15479
15709
  * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
@@ -15507,7 +15737,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
15507
15737
  child: AutomationConditionSchema
15508
15738
  }),
15509
15739
  AutomationConditionLeafSchema,
15510
- AutomationConditionExpressionSchema
15740
+ AutomationConditionExpressionSchema,
15741
+ AutomationConditionStationaryPresentSchema
15511
15742
  ]));
15512
15743
  /**
15513
15744
  * What starts a run.
@@ -15548,7 +15779,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15548
15779
  kind: literal("schedule"),
15549
15780
  cron: string$2().min(1).max(120)
15550
15781
  }),
15551
- object({ kind: literal("manual") })
15782
+ object({ kind: literal("manual") }),
15783
+ object({
15784
+ kind: literal("detection"),
15785
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
15786
+ * matches every raw class that maps to it; any other name matches
15787
+ * exactly. */
15788
+ classes: array(string$2().min(1)).min(1),
15789
+ deviceIds: array(number().int().nonnegative()).min(1),
15790
+ /** The track must have visited at least one of these zones. */
15791
+ zones: array(string$2().min(1)).min(1).optional(),
15792
+ /** Best-so-far confidence the track must reach. */
15793
+ minScore: number().min(0).max(1).optional()
15794
+ }),
15795
+ object({
15796
+ kind: literal("stationary"),
15797
+ deviceIds: array(number().int().nonnegative()).min(1),
15798
+ /** Class names; a macro class matches like the detection trigger. */
15799
+ classes: array(string$2().min(1)).min(1).optional(),
15800
+ /** The parked object must be in at least one of these zones. */
15801
+ zones: array(string$2().min(1)).min(1).optional(),
15802
+ /** `appeared`: it became parked. `departed`: the registry retired it.
15803
+ * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
15804
+ phase: _enum(["appeared", "departed"])
15805
+ }),
15806
+ object({
15807
+ kind: literal("interval"),
15808
+ minutes: number().int().min(1).max(1440)
15809
+ })
15552
15810
  ]);
15553
15811
  /**
15554
15812
  * One action step.
@@ -15568,28 +15826,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15568
15826
  * is called. The runner still receives a number and is untouched; the
15569
15827
  * resolution is the recipe's job, not the runner's.
15570
15828
  */
15829
+ var AutomationWaitStepSchema = object({
15830
+ kind: literal("wait"),
15831
+ seconds: number().min(0).max(300)
15832
+ });
15833
+ var AutomationCapStepSchema = object({
15834
+ kind: literal("cap"),
15835
+ /**
15836
+ * One device, several same-family devices (locks / climate family / …),
15837
+ * or `{ $var }` for the device that triggered the run. An array is
15838
+ * fan-out at codegen — each id gets the same cap.method call.
15839
+ */
15840
+ deviceId: union([
15841
+ number().int(),
15842
+ array(number().int()).min(1),
15843
+ object({ $var: string$2().min(1) })
15844
+ ]),
15845
+ cap: string$2().min(1),
15846
+ method: string$2().min(1),
15847
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
15848
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
15849
+ * the expression leaf. */
15850
+ args: record(string$2(), unknown()).optional()
15851
+ });
15852
+ /**
15853
+ * The steps an internal Action may contain: cap and wait, and nothing that can
15854
+ * call back into an Action — so an Action can never reach itself (no cycles).
15855
+ */
15856
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
15571
15857
  var AutomationActionSchema = discriminatedUnion("kind", [
15858
+ AutomationWaitStepSchema,
15859
+ AutomationCapStepSchema,
15572
15860
  object({
15573
- kind: literal("wait"),
15574
- seconds: number().min(0).max(300)
15575
- }),
15576
- object({
15577
- kind: literal("cap"),
15578
- /**
15579
- * One device, several same-family devices (locks / climate family / …),
15580
- * or `{ $var }` for the device that triggered the run. An array is
15581
- * fan-out at codegen — each id gets the same cap.method call.
15582
- */
15583
- deviceId: union([
15584
- number().int(),
15585
- array(number().int()).min(1),
15586
- object({ $var: string$2().min(1) })
15587
- ]),
15588
- cap: string$2().min(1),
15589
- method: string$2().min(1),
15590
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
15591
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
15592
- args: record(string$2(), unknown()).optional()
15861
+ kind: literal("action"),
15862
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
15863
+ actionId: string$2().min(1),
15864
+ /** Values for the action's declared variables; the others take their default. */
15865
+ variables: record(string$2(), union([
15866
+ string$2(),
15867
+ number(),
15868
+ boolean()
15869
+ ])).optional()
15593
15870
  }),
15594
15871
  object({
15595
15872
  kind: literal("code"),
@@ -15598,10 +15875,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15598
15875
  code: string$2().min(1).max(2e4)
15599
15876
  })
15600
15877
  ]);
15878
+ /**
15879
+ * How a trigger that arrives while the automation is busy (or cooling down) is
15880
+ * handled. One of the automation's execution modes, never a property of its
15881
+ * actions. Absent means `single`.
15882
+ *
15883
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
15884
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
15885
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
15886
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
15887
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
15888
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
15889
+ * wait, never the single/queued limits.
15890
+ */
15891
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
15892
+ object({ mode: literal("single") }),
15893
+ object({ mode: literal("restart") }),
15894
+ object({
15895
+ mode: literal("queued"),
15896
+ max: number().int().min(1).max(10)
15897
+ }),
15898
+ object({
15899
+ mode: literal("parallel"),
15900
+ max: number().int().min(1).max(10)
15901
+ }),
15902
+ object({
15903
+ mode: literal("cooldown"),
15904
+ minutes: number().int().min(1).max(1440)
15905
+ })
15906
+ ]);
15601
15907
  var AutomationRecipeSchema = object({
15602
15908
  triggers: array(AutomationTriggerSchema),
15603
15909
  conditions: AutomationConditionSchema.optional(),
15604
- actions: array(AutomationActionSchema)
15910
+ actions: array(AutomationActionSchema),
15911
+ runMode: AutomationRunModeSchema.optional(),
15912
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
15913
+ keepTraces: number().int().min(1).max(100).optional()
15914
+ });
15915
+ new Set([
15916
+ {
15917
+ id: "person",
15918
+ name: "Person"
15919
+ },
15920
+ {
15921
+ id: "vehicle",
15922
+ name: "Vehicle"
15923
+ },
15924
+ {
15925
+ id: "animal",
15926
+ name: "Animal"
15927
+ },
15928
+ {
15929
+ id: "package",
15930
+ name: "Package"
15931
+ }
15932
+ ].map((l) => l.id));
15933
+ var COCO_TO_MACRO = {
15934
+ mapping: {
15935
+ person: "person",
15936
+ bicycle: "vehicle",
15937
+ car: "vehicle",
15938
+ motorcycle: "vehicle",
15939
+ airplane: "vehicle",
15940
+ bus: "vehicle",
15941
+ train: "vehicle",
15942
+ truck: "vehicle",
15943
+ boat: "vehicle",
15944
+ bird: "animal",
15945
+ cat: "animal",
15946
+ dog: "animal",
15947
+ horse: "animal",
15948
+ sheep: "animal",
15949
+ cow: "animal",
15950
+ elephant: "animal",
15951
+ bear: "animal",
15952
+ zebra: "animal",
15953
+ giraffe: "animal",
15954
+ suitcase: "package",
15955
+ backpack: "package",
15956
+ handbag: "package"
15957
+ },
15958
+ preserveOriginal: false
15959
+ };
15960
+ new Set(Object.values(COCO_TO_MACRO.mapping));
15961
+ /**
15962
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
15963
+ * so the editor can paint every block green or red with its reason.
15964
+ *
15965
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
15966
+ * every condition node (containers and leaves, including the ones a
15967
+ * short-circuit never reached), the run-mode verdict and every action step —
15968
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
15969
+ * for an Action run). Skipped
15970
+ * evaluations are traced too, and identical consecutive skips collapse into a
15971
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
15972
+ *
15973
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
15974
+ * vocabulary so an Action run records the same step shape.
15975
+ */
15976
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
15977
+ var TraceValueSchema = union([
15978
+ string$2(),
15979
+ number(),
15980
+ boolean(),
15981
+ _null(),
15982
+ array(union([string$2(), number()]))
15983
+ ]);
15984
+ var AutomationRunOutcomeSchema = _enum([
15985
+ "ran",
15986
+ "skipped-trigger",
15987
+ "skipped-conditions",
15988
+ "skipped-cooldown",
15989
+ "skipped-overlap",
15990
+ "failed",
15991
+ "aborted"
15992
+ ]);
15993
+ /** `not-evaluated` = a short-circuit never reached the node. */
15994
+ var TraceNodeOutcomeSchema = _enum([
15995
+ "pass",
15996
+ "fail",
15997
+ "not-evaluated"
15998
+ ]);
15999
+ var AutomationTriggerTraceSchema = object({
16000
+ kind: string$2().min(1),
16001
+ outcome: _enum(["pass", "fail"]),
16002
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
16003
+ reason: string$2(),
16004
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
16005
+ vars: record(string$2(), TraceValueSchema)
16006
+ });
16007
+ var AutomationConditionTraceNodeSchema = object({
16008
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
16009
+ path: string$2().min(1),
16010
+ kind: _enum([
16011
+ "all",
16012
+ "any",
16013
+ "not",
16014
+ "condition",
16015
+ "expression",
16016
+ "stationary-present"
16017
+ ]),
16018
+ outcome: TraceNodeOutcomeSchema,
16019
+ reason: string$2(),
16020
+ deviceId: number().int().optional(),
16021
+ cap: string$2().optional(),
16022
+ fieldPath: string$2().optional(),
16023
+ operator: string$2().optional(),
16024
+ expected: TraceValueSchema.optional(),
16025
+ /** What the mirror held; `null` when it held nothing (unknown). */
16026
+ observed: TraceValueSchema.optional()
16027
+ });
16028
+ var AutomationRunVerdictSchema = object({
16029
+ mode: AutomationRunModeSchema,
16030
+ decision: _enum([
16031
+ "run",
16032
+ "queued",
16033
+ "restart",
16034
+ "drop",
16035
+ "cooldown",
16036
+ "bypass"
16037
+ ]),
16038
+ reason: string$2()
16039
+ });
16040
+ /** One executed (or not-run) step — shared by automations and Action runs. */
16041
+ var AutomationStepTraceSchema = object({
16042
+ /** `actions.<i>` */
16043
+ path: string$2().min(1),
16044
+ kind: string$2().min(1),
16045
+ cap: string$2().optional(),
16046
+ method: string$2().optional(),
16047
+ deviceId: number().int().optional(),
16048
+ startedAt: number().optional(),
16049
+ endedAt: number().optional(),
16050
+ outcome: _enum([
16051
+ "ok",
16052
+ "error",
16053
+ "not-run"
16054
+ ]),
16055
+ message: string$2().optional()
16056
+ });
16057
+ var AutomationRunTraceSchema = object({
16058
+ runId: string$2().min(1),
16059
+ startedAt: number(),
16060
+ endedAt: number(),
16061
+ trigger: AutomationTriggerTraceSchema,
16062
+ conditions: array(AutomationConditionTraceNodeSchema),
16063
+ verdict: AutomationRunVerdictSchema.optional(),
16064
+ steps: array(AutomationStepTraceSchema),
16065
+ outcome: AutomationRunOutcomeSchema,
16066
+ /** How many identical consecutive skips this trace stands for (>= 1). */
16067
+ repeat: number().int().min(1),
16068
+ /** When the latest of those repeats happened. */
16069
+ lastAt: number()
16070
+ });
16071
+ /**
16072
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
16073
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
16074
+ */
16075
+ var AutomationStateDocSchema = object({
16076
+ version: literal(1),
16077
+ lastRunStartedAt: number().nullable(),
16078
+ traces: array(AutomationRunTraceSchema)
16079
+ });
16080
+ /**
16081
+ * Actions — named, runnable things an operator (or an automation) can start,
16082
+ * declared as LIST ENTRIES, never as devices.
16083
+ *
16084
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
16085
+ * hub's `actions` record kind) or declared by an addon through the
16086
+ * `action-source` collection (a Home Assistant script, a scene …). The one
16087
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
16088
+ * catalog of its own (D3) — every `list` asks the providers live.
16089
+ *
16090
+ * This file is the data half: the entry schema, the variables an entry may
16091
+ * declare, and the pure resolution of the values a run was given. It holds no
16092
+ * runtime.
16093
+ */
16094
+ /** What a variable's value is. `select` takes one of `options`. */
16095
+ var ActionVariableTypeSchema = _enum([
16096
+ "string",
16097
+ "number",
16098
+ "boolean",
16099
+ "select"
16100
+ ]);
16101
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
16102
+ var ActionVariableValueSchema = union([
16103
+ string$2(),
16104
+ number(),
16105
+ boolean()
16106
+ ]);
16107
+ /**
16108
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
16109
+ * value) or `{{vars.name}}` (inside a string) in their args.
16110
+ */
16111
+ var ActionVariableSchema = object({
16112
+ name: string$2().regex(ACTION_VARIABLE_NAME_RE),
16113
+ label: string$2().min(1).max(120).optional(),
16114
+ type: ActionVariableTypeSchema,
16115
+ required: boolean().optional(),
16116
+ default: ActionVariableValueSchema.optional(),
16117
+ /** The allowed values of a `select`. */
16118
+ options: array(string$2().min(1)).min(1).optional()
16119
+ }).superRefine((v, ctx) => {
16120
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
16121
+ code: "custom",
16122
+ message: "a select variable needs options",
16123
+ path: ["options"]
16124
+ });
16125
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
16126
+ code: "custom",
16127
+ message: variableValueError(v, v.default) ?? "invalid default",
16128
+ path: ["default"]
16129
+ });
16130
+ });
16131
+ /** Why `value` is not a legal value for the variable, or null when it is. */
16132
+ function variableValueError(variable, value) {
16133
+ switch (variable.type) {
16134
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
16135
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
16136
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
16137
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
16138
+ }
16139
+ }
16140
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
16141
+ function isInternalActionId(actionId) {
16142
+ return actionId.startsWith("internal:") && actionId.length > 9;
16143
+ }
16144
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
16145
+ function addonIdOfActionId(actionId) {
16146
+ if (isInternalActionId(actionId)) return null;
16147
+ const at = actionId.indexOf(":");
16148
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
16149
+ }
16150
+ var ActionStatusSchema = object({
16151
+ isRunning: boolean(),
16152
+ /** Ms epoch the latest run STARTED; null when it never ran. */
16153
+ lastRunAt: number().nullable(),
16154
+ /** The latest run's failure; null on success or when it never ran. */
16155
+ lastError: string$2().nullable()
16156
+ });
16157
+ var ActionEntrySchema = object({
16158
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
16159
+ id: string$2().min(3),
16160
+ name: string$2().min(1).max(120),
16161
+ /** The addon that declared it; `core-blocks` for an internal one. */
16162
+ addonId: string$2().min(1),
16163
+ source: ActionSourceKindSchema,
16164
+ /** An addon-declared entry is edited where it was declared. */
16165
+ readOnly: boolean(),
16166
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
16167
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
16168
+ variables: array(ActionVariableSchema).max(20).optional(),
16169
+ status: ActionStatusSchema
16170
+ }).superRefine((entry, ctx) => {
16171
+ if (entry.source === "internal") {
16172
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
16173
+ code: "custom",
16174
+ message: "an internal action id is internal:<uuid>",
16175
+ path: ["id"]
16176
+ });
16177
+ return;
16178
+ }
16179
+ if (entry.steps !== void 0) ctx.addIssue({
16180
+ code: "custom",
16181
+ message: "only an internal action carries steps",
16182
+ path: ["steps"]
16183
+ });
16184
+ if (!entry.readOnly) ctx.addIssue({
16185
+ code: "custom",
16186
+ message: "an addon action is read-only here",
16187
+ path: ["readOnly"]
16188
+ });
16189
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
16190
+ code: "custom",
16191
+ message: "an addon action id is <addonId>:<localId>",
16192
+ path: ["id"]
16193
+ });
16194
+ });
16195
+ /** What an operator writes for an internal action. */
16196
+ var ActionInputSchema = object({
16197
+ name: string$2().min(1).max(120),
16198
+ steps: array(ActionStepSchema).min(1).max(50),
16199
+ variables: array(ActionVariableSchema).max(20).optional()
16200
+ });
16201
+ /** Who started a run. Absent = the operator pressed it. */
16202
+ var ActionRunOriginSchema = object({
16203
+ /** The automation block whose step started it. */
16204
+ blockId: string$2().min(1).optional() });
16205
+ /** `started` = the caller did not ask to wait. */
16206
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
16207
+ var ActionRunResultSchema = object({
16208
+ runId: string$2().min(1),
16209
+ outcome: ActionRunOutcomeSchema,
16210
+ /** Why it did not run, or what failed. */
16211
+ message: string$2().optional()
16212
+ });
16213
+ _enum([
16214
+ "not-found",
16215
+ "read-only",
16216
+ "invalid-variables",
16217
+ "invalid-steps",
16218
+ "not-linked",
16219
+ "busy"
16220
+ ]);
16221
+ object({
16222
+ actionId: string$2(),
16223
+ runId: string$2(),
16224
+ isRunning: boolean(),
16225
+ /** Set when the run ended. */
16226
+ outcome: AutomationRunOutcomeSchema.optional(),
16227
+ lastRunAt: number().nullable(),
16228
+ lastError: string$2().nullable()
15605
16229
  });
15606
16230
  /**
15607
16231
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -17196,7 +17820,22 @@ var BrokerProviderInfoSchema = object({
17196
17820
  */
17197
17821
  interactiveConnectPath: string$2().min(1).optional(),
17198
17822
  /** Relative hub path to disconnect the linked account (same popup pattern). */
17199
- interactiveDisconnectPath: string$2().min(1).optional()
17823
+ interactiveDisconnectPath: string$2().min(1).optional(),
17824
+ /**
17825
+ * The kind signs in through a server-held interactive session instead of
17826
+ * (or before) typed credentials. `basePath` is a hub-relative route set
17827
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
17828
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
17829
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
17830
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
17831
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
17832
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
17833
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
17834
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
17835
+ * offers it as the default path; the fields the session fills are
17836
+ * marked `filledBySignIn` in the kind's settings schema.
17837
+ */
17838
+ signIn: object({ basePath: string$2().min(1) }).optional()
17200
17839
  }))
17201
17840
  });
17202
17841
  var ListInputSchema = object({
@@ -18322,7 +18961,7 @@ var CompositionEventPlanSchema = object({
18322
18961
  stateField: string$2(),
18323
18962
  fieldKey: string$2()
18324
18963
  });
18325
- /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
18964
+ /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
18326
18965
  var CompositionProblemSchema = object({
18327
18966
  code: _enum([
18328
18967
  "reserved-variant",
@@ -18350,7 +18989,8 @@ var CompositionProblemSchema = object({
18350
18989
  "native-capability-replace",
18351
18990
  "mixed-quantity",
18352
18991
  "helper-mismatch",
18353
- "member-constraint"
18992
+ "member-constraint",
18993
+ "retention"
18354
18994
  ]),
18355
18995
  path: string$2(),
18356
18996
  message: string$2()
@@ -18584,13 +19224,17 @@ Object.freeze({
18584
19224
  latchedAt: {
18585
19225
  kind: "fixed",
18586
19226
  result: ["number", "null"]
19227
+ },
19228
+ schmitt: {
19229
+ kind: "fixed",
19230
+ result: ["boolean", "null"]
18587
19231
  }
18588
19232
  });
18589
19233
  /**
18590
19234
  * One catalog row per base reading a group helper can build (D944 §3).
18591
19235
  * Data, not a form default written into the block.
18592
19236
  */
18593
- var NOW = {
19237
+ var NOW$2 = {
18594
19238
  fieldPath: "lastFetchedAt",
18595
19239
  expr: "now"
18596
19240
  };
@@ -18604,6 +19248,8 @@ function numeric(input) {
18604
19248
  family: "numeric",
18605
19249
  deviceType: DeviceType.Sensor,
18606
19250
  memberDeviceType: DeviceType.Sensor,
19251
+ memberDeviceTypes: [DeviceType.Sensor],
19252
+ activeWhen: void 0,
18607
19253
  commandCaps: [],
18608
19254
  baseUnit: baseUnitOf(input.quantity),
18609
19255
  aggregates: NUMERIC_AGGREGATES,
@@ -18619,6 +19265,8 @@ function binary(input) {
18619
19265
  family: "binary",
18620
19266
  deviceType: DeviceType.Sensor,
18621
19267
  memberDeviceType: DeviceType.Sensor,
19268
+ memberDeviceTypes: [DeviceType.Sensor],
19269
+ activeWhen: void 0,
18622
19270
  commandCaps: [],
18623
19271
  quantity: "none",
18624
19272
  baseUnit: void 0,
@@ -18632,73 +19280,73 @@ var GROUP_TEMPLATES = [
18632
19280
  summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18633
19281
  role: DeviceRole.TemperatureSensor,
18634
19282
  outputCap: "temperature-sensor",
18635
- outputField: "celsius",
19283
+ outputField: "temperature",
18636
19284
  quantity: "temperature",
18637
19285
  defaultAggregate: "mean",
18638
19286
  empty: "null",
18639
- companions: [NOW]
19287
+ companions: [NOW$2]
18640
19288
  }),
18641
19289
  numeric({
18642
19290
  id: "humidity",
18643
19291
  summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18644
19292
  role: DeviceRole.HumiditySensor,
18645
19293
  outputCap: "humidity-sensor",
18646
- outputField: "percent",
19294
+ outputField: "humidity",
18647
19295
  quantity: "humidity",
18648
19296
  defaultAggregate: "mean",
18649
19297
  empty: "null",
18650
- companions: [NOW]
19298
+ companions: [NOW$2]
18651
19299
  }),
18652
19300
  numeric({
18653
19301
  id: "pressure",
18654
19302
  summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18655
19303
  role: DeviceRole.PressureSensor,
18656
19304
  outputCap: "pressure-sensor",
18657
- outputField: "hpa",
19305
+ outputField: "pressure",
18658
19306
  quantity: "pressure",
18659
19307
  defaultAggregate: "mean",
18660
19308
  empty: "null",
18661
- companions: [NOW]
19309
+ companions: [NOW$2]
18662
19310
  }),
18663
19311
  numeric({
18664
19312
  id: "illuminance",
18665
19313
  summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18666
19314
  role: DeviceRole.AmbientLightSensor,
18667
19315
  outputCap: "ambient-light-sensor",
18668
- outputField: "lux",
19316
+ outputField: "illuminance",
18669
19317
  quantity: "illuminance",
18670
19318
  defaultAggregate: "mean",
18671
19319
  empty: "null",
18672
- companions: [NOW]
19320
+ companions: [NOW$2]
18673
19321
  }),
18674
19322
  numeric({
18675
19323
  id: "power",
18676
19324
  summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18677
19325
  role: DeviceRole.PowerSensor,
18678
19326
  outputCap: "power-meter",
18679
- outputField: "watts",
19327
+ outputField: "power",
18680
19328
  quantity: "power",
18681
19329
  defaultAggregate: "sum",
18682
19330
  empty: "omit",
18683
- companions: [NOW]
19331
+ companions: [NOW$2]
18684
19332
  }),
18685
19333
  numeric({
18686
19334
  id: "energy",
18687
19335
  summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18688
19336
  role: DeviceRole.EnergySensor,
18689
19337
  outputCap: "power-meter",
18690
- outputField: "kwhTotal",
19338
+ outputField: "energy",
18691
19339
  quantity: "energy",
18692
19340
  defaultAggregate: "sum",
18693
19341
  empty: "omit",
18694
- companions: [NOW]
19342
+ companions: [NOW$2]
18695
19343
  }),
18696
19344
  numeric({
18697
19345
  id: "battery",
18698
19346
  summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18699
19347
  role: DeviceRole.BatterySensor,
18700
19348
  outputCap: "battery",
18701
- outputField: "percentage",
19349
+ outputField: "level",
18702
19350
  quantity: "percentage",
18703
19351
  defaultAggregate: "mean",
18704
19352
  empty: "null",
@@ -18726,7 +19374,7 @@ var GROUP_TEMPLATES = [
18726
19374
  quantity: "device",
18727
19375
  defaultAggregate: "mean",
18728
19376
  empty: "null",
18729
- companions: [NOW]
19377
+ companions: [NOW$2]
18730
19378
  }),
18731
19379
  binary({
18732
19380
  id: "motion",
@@ -18820,6 +19468,11 @@ var GROUP_TEMPLATES = [
18820
19468
  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.",
18821
19469
  deviceType: DeviceType.Switch,
18822
19470
  outputCap: "switch",
19471
+ outputField: "on",
19472
+ member: {
19473
+ cap: "switch",
19474
+ fieldPath: "on"
19475
+ },
18823
19476
  commandCaps: ["switch"]
18824
19477
  }),
18825
19478
  actuator({
@@ -18827,34 +19480,136 @@ var GROUP_TEMPLATES = [
18827
19480
  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.",
18828
19481
  deviceType: DeviceType.Light,
18829
19482
  outputCap: "switch",
19483
+ outputField: "on",
19484
+ member: {
19485
+ cap: "switch",
19486
+ fieldPath: "on"
19487
+ },
18830
19488
  commandCaps: [
18831
19489
  "switch",
18832
19490
  "brightness",
18833
19491
  "color"
18834
19492
  ]
19493
+ }),
19494
+ actuator({
19495
+ id: "fan",
19496
+ 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.",
19497
+ deviceType: DeviceType.Fan,
19498
+ outputCap: "switch",
19499
+ outputField: "on",
19500
+ member: {
19501
+ cap: "switch",
19502
+ fieldPath: "on"
19503
+ },
19504
+ commandCaps: ["switch", "fan-control"]
19505
+ }),
19506
+ actuator({
19507
+ id: "cover",
19508
+ 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.",
19509
+ deviceType: DeviceType.Cover,
19510
+ outputCap: "group-active",
19511
+ outputField: "active",
19512
+ member: {
19513
+ cap: "cover",
19514
+ fieldPath: "state"
19515
+ },
19516
+ activeWhen: [
19517
+ "open",
19518
+ "opening",
19519
+ "stopped"
19520
+ ],
19521
+ commandCaps: ["cover"]
19522
+ }),
19523
+ actuator({
19524
+ id: "valve",
19525
+ 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.",
19526
+ deviceType: DeviceType.Valve,
19527
+ outputCap: "group-active",
19528
+ outputField: "active",
19529
+ member: {
19530
+ cap: "valve",
19531
+ fieldPath: "state"
19532
+ },
19533
+ activeWhen: [
19534
+ "open",
19535
+ "opening",
19536
+ "stopped"
19537
+ ],
19538
+ commandCaps: ["valve"]
19539
+ }),
19540
+ actuator({
19541
+ id: "lock",
19542
+ 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.",
19543
+ deviceType: DeviceType.Lock,
19544
+ outputCap: "group-active",
19545
+ outputField: "active",
19546
+ member: {
19547
+ cap: "lock-control",
19548
+ fieldPath: "state"
19549
+ },
19550
+ activeWhen: ["locked"],
19551
+ commandCaps: ["lock-control"]
19552
+ }),
19553
+ actuator({
19554
+ id: "media-player",
19555
+ 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.",
19556
+ deviceType: DeviceType.MediaPlayer,
19557
+ outputCap: "group-active",
19558
+ outputField: "active",
19559
+ member: {
19560
+ cap: "media-player",
19561
+ fieldPath: "state"
19562
+ },
19563
+ activeWhen: ["playing", "buffering"],
19564
+ commandCaps: ["media-player"]
19565
+ }),
19566
+ actuator({
19567
+ id: "climate",
19568
+ 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.",
19569
+ deviceType: DeviceType.Thermostat,
19570
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
19571
+ outputCap: "group-active",
19572
+ outputField: "active",
19573
+ member: {
19574
+ cap: "climate-control",
19575
+ fieldPath: "mode"
19576
+ },
19577
+ activeWhen: [
19578
+ "heat",
19579
+ "cool",
19580
+ "auto",
19581
+ "heat_cool",
19582
+ "fan_only",
19583
+ "dry"
19584
+ ],
19585
+ commandCaps: ["climate-control"]
18835
19586
  })
18836
19587
  ];
18837
19588
  function actuator(input) {
18838
19589
  return {
18839
- ...input,
19590
+ id: input.id,
19591
+ summary: input.summary,
19592
+ deviceType: input.deviceType,
19593
+ outputCap: input.outputCap,
19594
+ outputField: input.outputField,
19595
+ commandCaps: input.commandCaps,
18840
19596
  family: "actuator",
18841
19597
  role: void 0,
18842
19598
  memberDeviceType: input.deviceType,
18843
- outputField: "on",
19599
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
19600
+ activeWhen: input.activeWhen,
18844
19601
  quantity: "none",
18845
19602
  baseUnit: void 0,
18846
19603
  aggregates: BINARY_AGGREGATES,
18847
19604
  defaultAggregate: "any",
18848
19605
  empty: "offline",
18849
- accept: [{
18850
- cap: "switch",
18851
- fieldPath: "on"
18852
- }],
19606
+ accept: [input.member],
18853
19607
  companions: [CHANGED]
18854
19608
  };
18855
19609
  }
18856
19610
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
18857
19611
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
19612
+ DeviceType.Camera;
18858
19613
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
18859
19614
  /**
18860
19615
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -19169,7 +19924,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
19169
19924
  kind: "mutation",
19170
19925
  auth: "admin",
19171
19926
  access: "view"
19172
- });
19927
+ }), method(object({
19928
+ blockId: string$2().min(1),
19929
+ trace: AutomationRunTraceSchema.optional(),
19930
+ runStartedAt: number().optional(),
19931
+ keepTraces: number().int().min(1).max(100).optional()
19932
+ }), _void(), {
19933
+ kind: "mutation",
19934
+ auth: "admin"
19935
+ }), method(object({ blockId: string$2().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string$2().min(1) }), object({
19936
+ traces: array(AutomationRunTraceSchema),
19937
+ lastError: string$2().nullable()
19938
+ }), { auth: "admin" });
19173
19939
  /**
19174
19940
  * `custom-model-registry` — collection cap exposing operator-registered
19175
19941
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -44078,7 +44844,7 @@ var NetworkAccessStatusSchema = object({
44078
44844
  });
44079
44845
  /**
44080
44846
  * Optional, richer endpoint shape returned by providers that expose
44081
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
44847
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44082
44848
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44083
44849
  * the originating provider config (mode + sourcePort) so the
44084
44850
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -44471,51 +45237,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
44471
45237
  kind: "mutation",
44472
45238
  auth: "admin"
44473
45239
  });
44474
- new Set([
44475
- {
44476
- id: "person",
44477
- name: "Person"
44478
- },
44479
- {
44480
- id: "vehicle",
44481
- name: "Vehicle"
44482
- },
44483
- {
44484
- id: "animal",
44485
- name: "Animal"
44486
- },
44487
- {
44488
- id: "package",
44489
- name: "Package"
44490
- }
44491
- ].map((l) => l.id));
44492
- var COCO_TO_MACRO = {
44493
- mapping: {
44494
- person: "person",
44495
- bicycle: "vehicle",
44496
- car: "vehicle",
44497
- motorcycle: "vehicle",
44498
- airplane: "vehicle",
44499
- bus: "vehicle",
44500
- train: "vehicle",
44501
- truck: "vehicle",
44502
- boat: "vehicle",
44503
- bird: "animal",
44504
- cat: "animal",
44505
- dog: "animal",
44506
- horse: "animal",
44507
- sheep: "animal",
44508
- cow: "animal",
44509
- elephant: "animal",
44510
- bear: "animal",
44511
- zebra: "animal",
44512
- giraffe: "animal",
44513
- suitcase: "package",
44514
- backpack: "package",
44515
- handbag: "package"
44516
- },
44517
- preserveOriginal: false
44518
- };
44519
45240
  var AUDIO_MACRO_LABELS = [
44520
45241
  {
44521
45242
  id: "speech",
@@ -46765,7 +47486,20 @@ var NcRuleInputSchema = object({
46765
47486
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
46766
47487
  * path does not run Zod defaults.
46767
47488
  */
46768
- groupingSec: number().int().min(5).max(60).optional()
47489
+ groupingSec: number().int().min(5).max(60).optional(),
47490
+ /**
47491
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47492
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47493
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47494
+ * same mark the Library writes, and records whose notification it was and
47495
+ * when. A channel with no buttons gets the same two choices as links.
47496
+ *
47497
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47498
+ * cap path): absent and `false` both mean off, which is what every rule
47499
+ * authored before this field meant. An editor turns it off by writing
47500
+ * `false`, so it needs no `clear` key.
47501
+ */
47502
+ askVerdict: boolean().optional()
46769
47503
  });
46770
47504
  /**
46771
47505
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -57069,9 +57803,9 @@ method(object({ nodeId: string$2().optional() }).optional(), array(AddonListItem
57069
57803
  */
57070
57804
  var AirQualitySensorStatusSchema = object({
57071
57805
  /** Carbon dioxide concentration in ppm. */
57072
- co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
57806
+ co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
57073
57807
  /** Total volatile organic compounds in ppb. */
57074
- vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57808
+ voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57075
57809
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
57076
57810
  pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
57077
57811
  /** Particulate matter ≤ 10 μm in µg/m³. */
@@ -57104,12 +57838,12 @@ var airQualitySensorCapability = {
57104
57838
  * History (D850): recorded series.
57105
57839
  */
57106
57840
  history: { fields: {
57107
- co2Ppm: {
57841
+ co2: {
57108
57842
  kind: "gauge",
57109
57843
  unit: "ppm",
57110
57844
  deadband: 5
57111
57845
  },
57112
- vocPpb: {
57846
+ voc: {
57113
57847
  kind: "gauge",
57114
57848
  unit: "ppb",
57115
57849
  deadband: 5
@@ -57140,7 +57874,7 @@ var airQualitySensorCapability = {
57140
57874
  */
57141
57875
  var AmbientLightSensorStatusSchema = object({
57142
57876
  /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
57143
- lux: number().min(0).nullable().meta(readingMeta("illuminance", {
57877
+ illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
57144
57878
  precision: 0,
57145
57879
  label: "Illuminance"
57146
57880
  })),
@@ -57169,7 +57903,7 @@ var ambientLightSensorCapability = {
57169
57903
  /**
57170
57904
  * History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
57171
57905
  */
57172
- history: { fields: { lux: {
57906
+ history: { fields: { illuminance: {
57173
57907
  kind: "gauge",
57174
57908
  unit: "lx",
57175
57909
  deadbandPct: 10,
@@ -57308,6 +58042,84 @@ var audioMetricsCapability = {
57308
58042
  */
57309
58043
  history: "none"
57310
58044
  };
58045
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58046
+ actionId: string$2().min(1),
58047
+ variables: record(string$2(), ActionVariableValueSchema).optional()
58048
+ }), _void(), {
58049
+ kind: "mutation",
58050
+ auth: "admin"
58051
+ }), method(object({ actionId: string$2().min(1) }), _void(), {
58052
+ kind: "mutation",
58053
+ auth: "admin"
58054
+ });
58055
+ /**
58056
+ * actions — the ONE surface for runnable actions, for the UI and for
58057
+ * automations. Provided by the core-blocks builtin on the hub.
58058
+ *
58059
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
58060
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
58061
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
58062
+ * the run status from the hub's `runs` ledger; there is no catalog here to
58063
+ * drift (D3).
58064
+ *
58065
+ * **Permissions follow the camera, as linked devices do.** An action is linked
58066
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
58067
+ * non-admin may `get` / `run` an action only through a camera they may see that
58068
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
58069
+ * gate sees it) and the provider refuses an action that camera does not link.
58070
+ * An admin, and an in-process caller (an automation block), may run any action.
58071
+ * Every refusal is typed (`[not-linked] …`) and logged.
58072
+ *
58073
+ * A second `run` of an action that is already running is REFUSED — traced and
58074
+ * logged — never queued.
58075
+ */
58076
+ var ActionIdInput = object({ actionId: string$2().min(1) });
58077
+ var ActionLinksSchema = object({
58078
+ deviceId: number().int().nonnegative(),
58079
+ actionIds: array(string$2().min(1)).max(100)
58080
+ });
58081
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58082
+ actionId: string$2().min(1),
58083
+ deviceId: number().int().nonnegative().optional()
58084
+ }), object({ action: ActionEntrySchema.nullable() }), {
58085
+ role: "viewer",
58086
+ auth: "protected",
58087
+ kind: "query",
58088
+ caller: "required"
58089
+ }), method(object({
58090
+ actionId: string$2().min(1),
58091
+ variables: record(string$2(), ActionVariableValueSchema).optional(),
58092
+ wait: boolean().optional(),
58093
+ origin: ActionRunOriginSchema.optional(),
58094
+ deviceId: number().int().nonnegative().optional()
58095
+ }), ActionRunResultSchema, {
58096
+ role: "viewer",
58097
+ auth: "protected",
58098
+ kind: "mutation",
58099
+ caller: "required"
58100
+ }), method(ActionIdInput, _void(), {
58101
+ kind: "mutation",
58102
+ auth: "admin"
58103
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
58104
+ kind: "mutation",
58105
+ auth: "admin"
58106
+ }), method(object({
58107
+ actionId: string$2().min(1),
58108
+ action: ActionInputSchema.partial()
58109
+ }), object({ action: ActionEntrySchema }), {
58110
+ kind: "mutation",
58111
+ auth: "admin"
58112
+ }), method(ActionIdInput, _void(), {
58113
+ kind: "mutation",
58114
+ auth: "admin"
58115
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
58116
+ role: "viewer",
58117
+ auth: "protected",
58118
+ kind: "query"
58119
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
58120
+ kind: "mutation",
58121
+ auth: "admin"
58122
+ });
57311
58123
  /**
57312
58124
  * Automation-control cap. Models HA `automation.*` entities on
57313
58125
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -57404,7 +58216,7 @@ var BatteryStatusSchema = object({
57404
58216
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
57405
58217
  * declines to band a non-finite reading, which is the correct shape.
57406
58218
  */
57407
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
58219
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
57408
58220
  /**
57409
58221
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
57410
58222
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -57414,7 +58226,7 @@ var BatteryStatusSchema = object({
57414
58226
  * `'unknown'` is the absence of an answer: the provider has not read the
57415
58227
  * power fields yet, or the firmware reported neither. Before it existed,
57416
58228
  * two unreadable fields produced `'none'`, and a camera nobody had reached
57417
- * was drawn "on battery alone" — the same D315 defect `percentage` closed
58229
+ * was drawn "on battery alone" — the same D315 defect `level` closed
57418
58230
  * by going nullable. Consumers SKIP it (no charger state published, no
57419
58231
  * glyph, no HomeKit ChargingState) rather than coerce it either way.
57420
58232
  */
@@ -57438,7 +58250,7 @@ var BatteryStatusSchema = object({
57438
58250
  * that only report charge. A second reading on this cap, not a second
57439
58251
  * `numeric-sensor` on the same device.
57440
58252
  */
57441
- volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
58253
+ voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
57442
58254
  /**
57443
58255
  * Ms epoch of the last time the device PROVED it was reachable — a
57444
58256
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -57472,7 +58284,7 @@ var BatteryStatusSchema = object({
57472
58284
  /**
57473
58285
  * True when the source is a BINARY low-battery indicator (HA
57474
58286
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
57475
- * charge level — `percentage` is then a coarse stand-in (100 = normal,
58287
+ * charge level — `level` is then a coarse stand-in (100 = normal,
57476
58288
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
57477
58289
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
57478
58290
  */
@@ -57485,21 +58297,22 @@ var BATTERY_DEVICE_TYPES = [
57485
58297
  DeviceType.Switch,
57486
58298
  DeviceType.PetFeeder,
57487
58299
  DeviceType.Container,
57488
- DeviceType.Thermostat
58300
+ DeviceType.Thermostat,
58301
+ DeviceType.EventEmitter
57489
58302
  ];
57490
58303
  /** Generic Customize target for the percentage reading (D936). */
57491
58304
  var batteryCustomize = {
57492
58305
  version: 1,
57493
58306
  targets: [{
57494
- id: "percentage",
58307
+ id: "level",
57495
58308
  cardinality: "scalar",
57496
58309
  vocabulary: {
57497
58310
  cap: "battery",
57498
- stateField: "percentage"
58311
+ stateField: "level"
57499
58312
  },
57500
58313
  deviceTypes: BATTERY_DEVICE_TYPES,
57501
58314
  apply: "add",
57502
- path: "percentage",
58315
+ path: "level",
57503
58316
  sources: [{
57504
58317
  kind: "field",
57505
58318
  valueKinds: ["number", "string"],
@@ -57593,7 +58406,7 @@ onStatusChanged: { data: object({
57593
58406
  schema: BatteryStatusSchema,
57594
58407
  kind: "push",
57595
58408
  empty: {
57596
- percentage: null,
58409
+ level: null,
57597
58410
  charging: "unknown",
57598
58411
  sleeping: false,
57599
58412
  lastUpdated: 0
@@ -57620,11 +58433,11 @@ onStatusChanged: { data: object({
57620
58433
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
57621
58434
  */
57622
58435
  history: { fields: {
57623
- percentage: {
58436
+ level: {
57624
58437
  kind: "gauge",
57625
58438
  unit: "%"
57626
58439
  },
57627
- volts: {
58440
+ voltage: {
57628
58441
  kind: "gauge",
57629
58442
  unit: "V"
57630
58443
  }
@@ -57684,14 +58497,14 @@ var binaryCapability = {
57684
58497
  * controls to this single-method surface.
57685
58498
  *
57686
58499
  * The cap is intentionally minimal: a single `setBrightness({deviceId,
57687
- * percentage})` mutation plus the auto-injected `getStatus`. Drivers
58500
+ * level})` mutation plus the auto-injected `getStatus`. Drivers
57688
58501
  * that expose richer controls (color temperature, scenes, schedules)
57689
58502
  * should surface those via the device's `getSettingsUISchema()`
57690
58503
  * instead of bloating this cap.
57691
58504
  */
57692
58505
  var BrightnessStatusSchema = object({
57693
58506
  /** Current level as 0..100 inclusive. Firmware-reported. */
57694
- percentage: number().min(0).max(100).meta(readingMeta("percentage")),
58507
+ level: number().min(0).max(100).meta(readingMeta("percentage")),
57695
58508
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
57696
58509
  lastChangedAt: number()
57697
58510
  });
@@ -57703,7 +58516,7 @@ var brightnessCapability = {
57703
58516
  deviceTypes: [DeviceType.Light],
57704
58517
  methods: { setBrightness: method(object({
57705
58518
  deviceId: number().int().nonnegative(),
57706
- percentage: number().min(0).max(100)
58519
+ level: number().min(0).max(100)
57707
58520
  }), _void(), {
57708
58521
  kind: "mutation",
57709
58522
  auth: "protected"
@@ -57716,7 +58529,7 @@ var brightnessCapability = {
57716
58529
  */
57717
58530
  onBrightnessChanged: { data: object({
57718
58531
  deviceId: number(),
57719
- percentage: number().min(0).max(100),
58532
+ level: number().min(0).max(100),
57720
58533
  lastChangedAt: number()
57721
58534
  }) } },
57722
58535
  status: {
@@ -57739,7 +58552,7 @@ onBrightnessChanged: { data: object({
57739
58552
  /**
57740
58553
  * History (D850): recorded series. Actuator tier: level keeps every change.
57741
58554
  */
57742
- history: { fields: { percentage: {
58555
+ history: { fields: { level: {
57743
58556
  kind: "gauge",
57744
58557
  unit: "%",
57745
58558
  minIntervalMs: 0
@@ -58353,11 +59166,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
58353
59166
  }),
58354
59167
  object({
58355
59168
  mode: literal("mired"),
58356
- mireds: number().int().min(50).max(1e3)
59169
+ colorTemperature: number().int().min(50).max(1e3)
58357
59170
  })
58358
59171
  ]);
58359
59172
  var ColorStatusSchema = object({
58360
- /** Active color mode — which of `rgb` / `hsv` / `mireds` reflects
59173
+ /** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
58361
59174
  * the bulb's current state. */
58362
59175
  mode: _enum([
58363
59176
  "rgb",
@@ -58369,7 +59182,7 @@ var ColorStatusSchema = object({
58369
59182
  /** Populated when `mode === 'hsv'`. */
58370
59183
  hsv: HsvTripletSchema.optional(),
58371
59184
  /** Populated when `mode === 'mired'`. */
58372
- mireds: number().int().optional().meta(readingMeta("colour-temperature")),
59185
+ colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
58373
59186
  /** Ms epoch of the last operator-driven change. */
58374
59187
  lastChangedAt: number()
58375
59188
  });
@@ -58401,7 +59214,7 @@ onColorChanged: { data: object({
58401
59214
  ]),
58402
59215
  rgb: RgbTripletSchema.optional(),
58403
59216
  hsv: HsvTripletSchema.optional(),
58404
- mireds: number().int().optional(),
59217
+ colorTemperature: number().int().optional(),
58405
59218
  lastChangedAt: number()
58406
59219
  }) } },
58407
59220
  status: {
@@ -59278,11 +60091,11 @@ var DishwasherStatusSchema = object({
59278
60091
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
59279
60092
  availablePrograms: array(string$2()),
59280
60093
  /** Seconds remaining on the active cycle, or null when not running / not known. */
59281
- remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60094
+ remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59282
60095
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
59283
- delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60096
+ delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59284
60097
  /** Cycle completion 0..100, or null when not applicable. */
59285
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60098
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59286
60099
  /** True when the door is reported open; null when the door state is not known. */
59287
60100
  doorOpen: boolean().nullable(),
59288
60101
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -59501,14 +60314,14 @@ var dishwasherCapability = {
59501
60314
  runtimeState: DishwasherStatusSchema,
59502
60315
  durability: "session",
59503
60316
  history: { fields: {
59504
- progressPercent: {
60317
+ progress: {
59505
60318
  kind: "gauge",
59506
60319
  unit: "%",
59507
60320
  deadband: 2
59508
60321
  },
59509
60322
  state: { kind: "enum" },
59510
60323
  operationState: { kind: "enum" },
59511
- remainingSeconds: {
60324
+ remaining: {
59512
60325
  kind: "gauge",
59513
60326
  unit: "s",
59514
60327
  minIntervalMs: 6e4
@@ -60099,7 +60912,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
60099
60912
  var FanControlStatusSchema = object({
60100
60913
  /** Active speed as 0..100 inclusive. Null when the device has no
60101
60914
  * speed surface (single-speed fan). */
60102
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60915
+ speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60103
60916
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
60104
60917
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
60105
60918
  percentageStep: number().positive().optional().meta(readingMeta("percentage")),
@@ -60126,7 +60939,7 @@ var fanControlCapability = {
60126
60939
  methods: {
60127
60940
  setPercentage: method(object({
60128
60941
  deviceId: number().int().nonnegative(),
60129
- percentage: number().min(0).max(100)
60942
+ speed: number().min(0).max(100)
60130
60943
  }), _void(), {
60131
60944
  kind: "mutation",
60132
60945
  auth: "protected"
@@ -60159,7 +60972,7 @@ var fanControlCapability = {
60159
60972
  },
60160
60973
  /**
60161
60974
  * Runtime-state slice — mirrored by the kernel. UI fan speed
60162
- * sliders read `percentage` for live updates.
60975
+ * sliders read `speed` for live updates.
60163
60976
  */
60164
60977
  runtimeState: FanControlStatusSchema,
60165
60978
  /**
@@ -60173,7 +60986,7 @@ var fanControlCapability = {
60173
60986
  * History (D850): recorded series. Actuator tier: speed keeps every change.
60174
60987
  */
60175
60988
  history: { fields: {
60176
- percentage: {
60989
+ speed: {
60177
60990
  kind: "gauge",
60178
60991
  unit: "%",
60179
60992
  minIntervalMs: 0
@@ -60362,7 +61175,17 @@ var groupCommandsCapability = {
60362
61175
  scope: "device",
60363
61176
  deviceNative: true,
60364
61177
  mode: "singleton",
60365
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
61178
+ deviceTypes: [
61179
+ DeviceType.Switch,
61180
+ DeviceType.Light,
61181
+ DeviceType.Fan,
61182
+ DeviceType.Cover,
61183
+ DeviceType.Valve,
61184
+ DeviceType.Lock,
61185
+ DeviceType.MediaPlayer,
61186
+ DeviceType.Thermostat,
61187
+ DeviceType.Climate
61188
+ ],
60366
61189
  methods: {},
60367
61190
  status: {
60368
61191
  schema: GroupCommandsSchema,
@@ -60380,6 +61203,49 @@ var groupCommandsCapability = {
60380
61203
  history: "none"
60381
61204
  };
60382
61205
  /**
61206
+ * Whether a helper actuator group counts as active (D944): at least one
61207
+ * included member is, or every included member is. What "active" means is the
61208
+ * type's: a cover is open, a lock is locked, a media player is playing, a
61209
+ * climate device is not off. A group of lights, switches and fans reports
61210
+ * `switch.on` instead; this cap is for the types whose own state is not a
61211
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
61212
+ * empty included set takes the device offline, never `active: false`.
61213
+ */
61214
+ var GroupActiveSchema = object({
61215
+ active: boolean().meta({ label: "Active" }),
61216
+ /** Ms epoch when the slice was last updated. */
61217
+ lastChangedAt: number()
61218
+ });
61219
+ var groupActiveCapability = {
61220
+ name: "group-active",
61221
+ scope: "device",
61222
+ deviceNative: true,
61223
+ mode: "singleton",
61224
+ deviceTypes: [
61225
+ DeviceType.Cover,
61226
+ DeviceType.Valve,
61227
+ DeviceType.Lock,
61228
+ DeviceType.MediaPlayer,
61229
+ DeviceType.Climate,
61230
+ DeviceType.Thermostat
61231
+ ],
61232
+ methods: {},
61233
+ status: {
61234
+ schema: GroupActiveSchema,
61235
+ kind: "push"
61236
+ },
61237
+ runtimeState: GroupActiveSchema,
61238
+ /**
61239
+ * Runtime-state durability: **session** — recomputed from the members on the
61240
+ * next source event; a restored value would outlive the members it named.
61241
+ *
61242
+ * See `RuntimeStateDurability`. Enforced by
61243
+ * `scripts/check-runtime-state-durability.ts`.
61244
+ */
61245
+ durability: "session",
61246
+ history: "none"
61247
+ };
61248
+ /**
60383
61249
  * How many members of a helper group answered (D944). Counts are integers,
60384
61250
  * not measurements: zero included is a real count, never a stand-in reading.
60385
61251
  * The reading itself stays on the output capability. History is none — the
@@ -60410,7 +61276,14 @@ var groupStatusCapability = {
60410
61276
  deviceTypes: [
60411
61277
  DeviceType.Sensor,
60412
61278
  DeviceType.Switch,
60413
- DeviceType.Light
61279
+ DeviceType.Light,
61280
+ DeviceType.Fan,
61281
+ DeviceType.Cover,
61282
+ DeviceType.Valve,
61283
+ DeviceType.Lock,
61284
+ DeviceType.MediaPlayer,
61285
+ DeviceType.Thermostat,
61286
+ DeviceType.Climate
60414
61287
  ],
60415
61288
  methods: {},
60416
61289
  status: {
@@ -60558,7 +61431,7 @@ var humidifierCapability = {
60558
61431
  */
60559
61432
  var HumiditySensorStatusSchema = object({
60560
61433
  /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
60561
- percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
61434
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
60562
61435
  precision: 0,
60563
61436
  label: "Humidity"
60564
61437
  })),
@@ -60587,7 +61460,7 @@ var humiditySensorCapability = {
60587
61460
  /**
60588
61461
  * History (D850): recorded series.
60589
61462
  */
60590
- history: { fields: { percent: {
61463
+ history: { fields: { humidity: {
60591
61464
  kind: "gauge",
60592
61465
  unit: "%",
60593
61466
  deadband: .5
@@ -61227,7 +62100,7 @@ var LawnMowerControlStatusSchema = object({
61227
62100
  batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61228
62101
  /** 0..100 mowing-completion percentage of the current task, or null when no
61229
62102
  * task is active / progress is unavailable. */
61230
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62103
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61231
62104
  /** Current device/event code (dynamic — mostly status, sometimes an error),
61232
62105
  * or null when unknown. */
61233
62106
  currentCode: number().nullable().meta(readingMeta("none")),
@@ -61683,7 +62556,14 @@ var lockControlCapability = {
61683
62556
  }),
61684
62557
  unlock: method(object({
61685
62558
  deviceId: number().int().nonnegative(),
61686
- code: string$2().min(1).optional()
62559
+ code: string$2().min(1).optional(),
62560
+ /**
62561
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62562
+ * with one call is refused unless the caller says it was confirmed. A
62563
+ * single lock ignores it. The refusal lives in the callee, so no
62564
+ * automation or API caller reaches the doors by skipping a dialog.
62565
+ */
62566
+ confirm: boolean().optional()
61687
62567
  }), _void(), {
61688
62568
  kind: "mutation",
61689
62569
  auth: "protected"
@@ -61720,6 +62600,44 @@ var lockControlCapability = {
61720
62600
  volatileStateFields: ["lastChangedAt"]
61721
62601
  };
61722
62602
  /**
62603
+ * A monotonic total between resets (D944). Energy uses `power-meter.energy`
62604
+ * so dashboards keep one series; every other instantaneous reading lands here.
62605
+ * `value` is the time integral in the source quantity's accumulated base
62606
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
62607
+ */
62608
+ var MeterStatusSchema = object({
62609
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
62610
+ lastFetchedAt: number()
62611
+ });
62612
+ var meterCapability = {
62613
+ name: "meter",
62614
+ scope: "device",
62615
+ deviceNative: true,
62616
+ mode: "singleton",
62617
+ deviceTypes: [DeviceType.Sensor],
62618
+ methods: {},
62619
+ status: {
62620
+ schema: MeterStatusSchema,
62621
+ kind: "push"
62622
+ },
62623
+ runtimeState: MeterStatusSchema,
62624
+ /**
62625
+ * Runtime-state durability: **restored** — the total is a cache of the
62626
+ * history integral. A restart shows it only until the history pass
62627
+ * recomputes the open period; it is not an in-memory buffer.
62628
+ *
62629
+ * See `RuntimeStateDurability`. Enforced by
62630
+ * `scripts/check-runtime-state-durability.ts`.
62631
+ */
62632
+ durability: "restored",
62633
+ history: { fields: { value: {
62634
+ kind: "counter",
62635
+ deadband: .001,
62636
+ minIntervalMs: 6e4
62637
+ } } },
62638
+ volatileStateFields: ["lastFetchedAt"]
62639
+ };
62640
+ /**
61723
62641
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
61724
62642
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
61725
62643
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -61776,10 +62694,10 @@ var MediaPlayerStatusSchema = object({
61776
62694
  currentMedia: MediaInfoSchema.nullable(),
61777
62695
  /** Current playback position in ms. Null when not seekable or
61778
62696
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
61779
- positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62697
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
61780
62698
  /** Total duration of the current media in ms. Null when unknown
61781
62699
  * (live stream) or nothing is playing. */
61782
- durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62700
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
61783
62701
  /** Shuffle toggle. Null when no shuffle surface.
61784
62702
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
61785
62703
  shuffle: boolean().nullable(),
@@ -61818,7 +62736,7 @@ var mediaPlayerCapability = {
61818
62736
  }),
61819
62737
  seek: method(object({
61820
62738
  deviceId: number().int().nonnegative(),
61821
- positionMs: number().int().nonnegative()
62739
+ position: number().int().nonnegative()
61822
62740
  }), _void(), {
61823
62741
  kind: "mutation",
61824
62742
  auth: "protected"
@@ -61890,7 +62808,7 @@ var mediaPlayerCapability = {
61890
62808
  /**
61891
62809
  * History (D850): volume (a level an operator or an automation sets; the
61892
62810
  * default 10 s floor folds a slider drag into its end value) and mute.
61893
- * `positionMs` is not recorded: it moves continuously while playing.
62811
+ * `position` is not recorded: it moves continuously while playing.
61894
62812
  */
61895
62813
  history: { fields: {
61896
62814
  volumeLevel: {
@@ -62852,9 +63770,9 @@ var NetworkLinkStatusSchema = object({
62852
63770
  * one whose reading has not landed must not be drawn at 0 %. Consumers
62853
63771
  * SKIP a null rather than coerce it.
62854
63772
  */
62855
- signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63773
+ signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62856
63774
  /** Raw received signal strength in dBm, when the firmware reports one. */
62857
- rssiDbm: number().optional().meta(readingMeta("signal-level")),
63775
+ rssi: number().optional().meta(readingMeta("signal-level")),
62858
63776
  /** Network name of a wireless link, when the firmware reports it. */
62859
63777
  ssid: string$2().optional(),
62860
63778
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
@@ -62890,7 +63808,7 @@ onStatusChanged: { data: object({
62890
63808
  kind: "push",
62891
63809
  empty: {
62892
63810
  type: "unknown",
62893
- signalPercent: null,
63811
+ signal: null,
62894
63812
  lastUpdated: 0
62895
63813
  }
62896
63814
  },
@@ -62913,13 +63831,13 @@ onStatusChanged: { data: object({
62913
63831
  * History (D850): recorded series.
62914
63832
  */
62915
63833
  history: { fields: {
62916
- signalPercent: {
63834
+ signal: {
62917
63835
  kind: "gauge",
62918
63836
  unit: "%",
62919
63837
  deadband: 3,
62920
63838
  minIntervalMs: 6e4
62921
63839
  },
62922
- rssiDbm: {
63840
+ rssi: {
62923
63841
  kind: "gauge",
62924
63842
  unit: "dBm",
62925
63843
  deadband: 2,
@@ -63664,10 +64582,20 @@ var PetFeederDeviceStatusSchema = _enum([
63664
64582
  ]);
63665
64583
  var gramsPortion = number().int().min(4).max(200);
63666
64584
  var PetFeederStatusSchema = object({
63667
- /** Food currently in the bowl (grams). Null when the device has not
63668
- * reported a reading yet. On dual-hopper models this is the combined
63669
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
64585
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
64586
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
64587
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
64588
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
64589
+ * the device has not reported yet. */
63670
64590
  foodLevel: number().nullable().meta(readingMeta("none")),
64591
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
64592
+ * feeder's own units. Null when the model reports none. */
64593
+ bowlFood: number().nullable().meta(readingMeta("none")),
64594
+ /** True while the pet is eating (`state.eating`). */
64595
+ eating: boolean(),
64596
+ /** Whether `markFoodReplenished` does anything on this model. When false
64597
+ * the client must not offer it. */
64598
+ supportsMarkRefilled: boolean(),
63671
64599
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
63672
64600
  * single-hopper models. */
63673
64601
  food1: number().nullable().meta(readingMeta("none")),
@@ -63675,8 +64603,8 @@ var PetFeederStatusSchema = object({
63675
64603
  * single-hopper models. */
63676
64604
  food2: number().nullable().meta(readingMeta("none")),
63677
64605
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
63678
- * (`device_class: problem`, on = low). True when the bowl is empty /
63679
- * below the feeder's low threshold. */
64606
+ * (`device_class: problem`, on = low). True when the tank reports empty,
64607
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
63680
64608
  lowFood: boolean(),
63681
64609
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
63682
64610
  * device has no battery reading. */
@@ -63744,7 +64672,9 @@ var petFeederCapability = {
63744
64672
  kind: "mutation",
63745
64673
  auth: "protected"
63746
64674
  }),
63747
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
64675
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
64676
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
64677
+ * change any sensor reading. */
63748
64678
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
63749
64679
  kind: "mutation",
63750
64680
  auth: "protected"
@@ -63817,6 +64747,7 @@ var petFeederCapability = {
63817
64747
  */
63818
64748
  history: { fields: {
63819
64749
  foodLevel: { kind: "gauge" },
64750
+ bowlFood: { kind: "gauge" },
63820
64751
  food1: { kind: "gauge" },
63821
64752
  food2: { kind: "gauge" },
63822
64753
  batteryPower: {
@@ -64226,27 +65157,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
64226
65157
  * in real-world devices.
64227
65158
  */
64228
65159
  var PowerMeterStatusSchema = object({
64229
- /** Instantaneous power draw in watts. */
64230
- watts: number().optional().meta(readingMeta("power", { label: "Power" })),
65160
+ /** Instantaneous power draw (watts). */
65161
+ power: number().optional().meta(readingMeta("power", { label: "Power" })),
64231
65162
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64232
- kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64233
- /** Voltage in volts. */
64234
- volts: number().optional().meta(readingMeta("voltage")),
65163
+ energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
65164
+ /** Voltage (volts). */
65165
+ voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64235
65166
  /** Current in amperes. */
64236
- amps: number().optional().meta(readingMeta("current")),
65167
+ current: number().optional().meta(readingMeta("current", { label: "Current" })),
64237
65168
  /** Apparent power in volt-amperes. */
64238
- va: number().optional().meta(readingMeta("apparent-power")),
65169
+ apparentPower: number().optional().meta(readingMeta("apparent-power")),
64239
65170
  /** Power factor (0..1) if reported. */
64240
65171
  powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
64241
65172
  /** Ms epoch when the slice was last updated. */
64242
65173
  lastFetchedAt: number()
64243
65174
  });
65175
+ /**
65176
+ * Loads that draw metered mains power (D951). The reading is a feature of the
65177
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
65178
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
65179
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
65180
+ * camera and the like are left out: their draw is not what anyone meters.
65181
+ */
65182
+ var POWER_METER_DEVICE_TYPES = [
65183
+ DeviceType.Sensor,
65184
+ DeviceType.Switch,
65185
+ DeviceType.Light,
65186
+ DeviceType.Fan,
65187
+ DeviceType.Climate,
65188
+ DeviceType.Thermostat,
65189
+ DeviceType.Humidifier,
65190
+ DeviceType.WaterHeater,
65191
+ DeviceType.MediaPlayer,
65192
+ DeviceType.Washer,
65193
+ DeviceType.Dryer,
65194
+ DeviceType.Dishwasher,
65195
+ DeviceType.Vacuum,
65196
+ DeviceType.LawnMower,
65197
+ DeviceType.PetFeeder
65198
+ ];
65199
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
65200
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
65201
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
65202
+ function powerTarget(id, unit) {
65203
+ return {
65204
+ id,
65205
+ cardinality: "scalar",
65206
+ vocabulary: {
65207
+ cap: "power-meter",
65208
+ stateField: id
65209
+ },
65210
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
65211
+ apply: "add",
65212
+ path: id,
65213
+ sources: [{
65214
+ kind: "numeric",
65215
+ requiredUnit: unit,
65216
+ transforms: [{ kind: "identity" }]
65217
+ }],
65218
+ companions: [{
65219
+ kind: "clock",
65220
+ path: "lastFetchedAt",
65221
+ when: "always"
65222
+ }],
65223
+ sourceReport: { status: "assignment-status" },
65224
+ advancedExpression: true
65225
+ };
65226
+ }
65227
+ /** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
65228
+ var powerMeterCustomize = {
65229
+ version: 1,
65230
+ targets: [
65231
+ powerTarget("power", "W"),
65232
+ powerTarget("energy", "kWh"),
65233
+ powerTarget("voltage", "V"),
65234
+ powerTarget("current", "A")
65235
+ ]
65236
+ };
64244
65237
  var powerMeterCapability = {
64245
65238
  name: "power-meter",
64246
65239
  scope: "device",
64247
65240
  deviceNative: true,
64248
65241
  mode: "singleton",
64249
- deviceTypes: [DeviceType.Sensor],
65242
+ deviceTypes: POWER_METER_DEVICE_TYPES,
64250
65243
  methods: {},
64251
65244
  status: {
64252
65245
  schema: PowerMeterStatusSchema,
@@ -64254,41 +65247,41 @@ var powerMeterCapability = {
64254
65247
  },
64255
65248
  runtimeState: PowerMeterStatusSchema,
64256
65249
  /**
64257
- * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
65250
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
64258
65251
  *
64259
65252
  * See `RuntimeStateDurability`. Enforced by
64260
65253
  * `scripts/check-runtime-state-durability.ts`.
64261
65254
  */
64262
65255
  durability: "restored",
64263
65256
  /**
64264
- * History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
65257
+ * History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
64265
65258
  */
64266
65259
  history: { fields: {
64267
- watts: {
65260
+ power: {
64268
65261
  kind: "gauge",
64269
65262
  unit: "W",
64270
65263
  deadband: 1,
64271
65264
  minIntervalMs: 3e4
64272
65265
  },
64273
- kwhTotal: {
65266
+ energy: {
64274
65267
  kind: "counter",
64275
65268
  unit: "kWh",
64276
65269
  deadband: .01,
64277
65270
  minIntervalMs: 6e4
64278
65271
  },
64279
- volts: {
65272
+ voltage: {
64280
65273
  kind: "gauge",
64281
65274
  unit: "V",
64282
65275
  deadband: 1,
64283
65276
  minIntervalMs: 3e4
64284
65277
  },
64285
- amps: {
65278
+ current: {
64286
65279
  kind: "gauge",
64287
65280
  unit: "A",
64288
65281
  deadband: .05,
64289
65282
  minIntervalMs: 3e4
64290
65283
  },
64291
- va: {
65284
+ apparentPower: {
64292
65285
  kind: "gauge",
64293
65286
  unit: "VA",
64294
65287
  deadband: 1,
@@ -64303,7 +65296,8 @@ var powerMeterCapability = {
64303
65296
  } },
64304
65297
  /** Clock fields: written, but excluded from the compare that decides
64305
65298
  * whether persisting is worth a SQLite commit. */
64306
- volatileStateFields: ["lastFetchedAt"]
65299
+ volatileStateFields: ["lastFetchedAt"],
65300
+ customize: powerMeterCustomize
64307
65301
  };
64308
65302
  /**
64309
65303
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -64337,9 +65331,6 @@ var PresenceStatusSchema = object({
64337
65331
  location: string$2().nullable(),
64338
65332
  /** GPS coordinates when available. Null otherwise. */
64339
65333
  gps: GpsLocationSchema.nullable(),
64340
- /** Optional battery level of the tracking device (0..100). Null
64341
- * when not reported. */
64342
- batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64343
65334
  /** Ms epoch when the slice was last updated. */
64344
65335
  lastChangedAt: number()
64345
65336
  });
@@ -64368,14 +65359,7 @@ var presenceCapability = {
64368
65359
  * `scripts/check-runtime-state-durability.ts`.
64369
65360
  */
64370
65361
  durability: "restored",
64371
- /**
64372
- * History (D850): recorded series.
64373
- */
64374
- history: { fields: { batteryPercent: {
64375
- kind: "gauge",
64376
- unit: "%",
64377
- deadband: 1
64378
- } } },
65362
+ history: "none",
64379
65363
  /** Clock fields: written, but excluded from the compare that decides
64380
65364
  * whether persisting is worth a SQLite commit. */
64381
65365
  volatileStateFields: ["lastChangedAt"]
@@ -64390,7 +65374,7 @@ var presenceCapability = {
64390
65374
  */
64391
65375
  var PressureSensorStatusSchema = object({
64392
65376
  /** Current pressure in hPa. `null` when the sample was refused (D393). */
64393
- hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
65377
+ pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
64394
65378
  /** Ms epoch when the slice was last updated. */
64395
65379
  lastFetchedAt: number()
64396
65380
  });
@@ -64416,7 +65400,7 @@ var pressureSensorCapability = {
64416
65400
  /**
64417
65401
  * History (D850): recorded series.
64418
65402
  */
64419
- history: { fields: { hpa: {
65403
+ history: { fields: { pressure: {
64420
65404
  kind: "gauge",
64421
65405
  unit: "hPa",
64422
65406
  deadband: .1
@@ -64984,6 +65968,8 @@ var runningCustomize = {
64984
65968
  stateField: "running"
64985
65969
  },
64986
65970
  deviceTypes: [
65971
+ DeviceType.Washer,
65972
+ DeviceType.Dryer,
64987
65973
  DeviceType.Dishwasher,
64988
65974
  DeviceType.Vacuum,
64989
65975
  DeviceType.LawnMower,
@@ -67473,7 +68459,8 @@ var switchCapability = {
67473
68459
  deviceTypes: [
67474
68460
  DeviceType.Switch,
67475
68461
  DeviceType.Siren,
67476
- DeviceType.Light
68462
+ DeviceType.Light,
68463
+ DeviceType.Fan
67477
68464
  ],
67478
68465
  methods: { setState: method(object({
67479
68466
  deviceId: number(),
@@ -68112,7 +69099,7 @@ var tamperCapability = {
68112
69099
  */
68113
69100
  var TemperatureSensorStatusSchema = object({
68114
69101
  /** Current temperature in Celsius. `null` when the sample was refused (D393). */
68115
- celsius: number().nullable().meta(readingMeta("temperature", {
69102
+ temperature: number().nullable().meta(readingMeta("temperature", {
68116
69103
  precision: 1,
68117
69104
  label: "Temperature"
68118
69105
  })),
@@ -68141,7 +69128,7 @@ var temperatureSensorCapability = {
68141
69128
  /**
68142
69129
  * History (D850): recorded series.
68143
69130
  */
68144
- history: { fields: { celsius: {
69131
+ history: { fields: { temperature: {
68145
69132
  kind: "gauge",
68146
69133
  unit: "°C",
68147
69134
  deadband: .1
@@ -68438,7 +69425,7 @@ var VacuumControlStatusSchema = object({
68438
69425
  /** Speed tokens the hardware accepts — drives the UI selector. */
68439
69426
  availableFanSpeeds: array(string$2()),
68440
69427
  /** 0..100 cleaning-completion percentage of the current task, or null. */
68441
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
69428
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
68442
69429
  /** Current error code (0 / null = no error). */
68443
69430
  errorCode: number().nullable().meta(readingMeta("none")),
68444
69431
  /** Human label for {@link errorCode}, or null when none / undecodable. */
@@ -69686,8 +70673,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
69686
70673
  kind: "mutation",
69687
70674
  auth: "admin"
69688
70675
  });
70676
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
69689
70677
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
69690
- 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;
70678
+ 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;
69691
70679
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
69692
70680
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
69693
70681
  new Set(CAP_NAMES);
@@ -70016,6 +71004,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70016
71004
  featureProbe: featureProbeCapability,
70017
71005
  flood: floodCapability,
70018
71006
  gas: gasCapability,
71007
+ groupActive: groupActiveCapability,
70019
71008
  groupCommands: groupCommandsCapability,
70020
71009
  groupStatus: groupStatusCapability,
70021
71010
  heatingValve: heatingValveCapability,
@@ -70027,6 +71016,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70027
71016
  lawnMowerControl: lawnMowerControlCapability,
70028
71017
  lockControl: lockControlCapability,
70029
71018
  mediaPlayer: mediaPlayerCapability,
71019
+ meter: meterCapability,
70030
71020
  motion: motionCapability,
70031
71021
  motionTrigger: motionTriggerCapability,
70032
71022
  motionZones: motionZonesCapability,
@@ -70089,7 +71079,7 @@ var BaseDevice = class {
70089
71079
  * `device-local-state.ts`. Drivers access via:
70090
71080
  *
70091
71081
  * `this.state.battery.sleeping = true` // patches the battery slice
70092
- * `const pct = this.state.battery.percentage` // reads the battery slice
71082
+ * `const pct = this.state.battery.level` // reads the battery slice
70093
71083
  * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
70094
71084
  *
70095
71085
  * Adding a new cap with `runtimeState:` automatically extends this
@@ -70129,7 +71119,7 @@ var BaseDevice = class {
70129
71119
  * shape; drivers don't redeclare it, they just write through.
70130
71120
  *
70131
71121
  * Read: `this.runtimeState.getCapState('battery')` →
70132
- * `{percentage, charging, sleeping, lastUpdated}` for any
71122
+ * `{level, charging, sleeping, lastUpdated}` for any
70133
71123
  * provider that registers `batteryCapability`.
70134
71124
  * Write: `this.runtimeState.setCapState('battery', { … })`.
70135
71125
  *
@@ -71286,7 +72276,7 @@ var BaseDeviceProvider = class extends BaseAddon {
71286
72276
  return toDeviceSummary(device, this.addonId);
71287
72277
  }
71288
72278
  };
71289
- 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;
72279
+ 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;
71290
72280
  new Set(Object.values(DeviceType));
71291
72281
  Object.freeze({
71292
72282
  "accessories.getStatus": {
@@ -71303,6 +72293,104 @@ Object.freeze({
71303
72293
  access: "create",
71304
72294
  role: "admin"
71305
72295
  },
72296
+ "actions.create": {
72297
+ capName: "actions",
72298
+ capScope: "system",
72299
+ addonId: null,
72300
+ access: "create",
72301
+ role: "admin"
72302
+ },
72303
+ "actions.delete": {
72304
+ capName: "actions",
72305
+ capScope: "system",
72306
+ addonId: null,
72307
+ access: "delete",
72308
+ role: "admin"
72309
+ },
72310
+ "actions.get": {
72311
+ capName: "actions",
72312
+ capScope: "system",
72313
+ addonId: null,
72314
+ access: "view",
72315
+ role: "viewer"
72316
+ },
72317
+ "actions.getLinks": {
72318
+ capName: "actions",
72319
+ capScope: "system",
72320
+ addonId: null,
72321
+ access: "view",
72322
+ role: "admin"
72323
+ },
72324
+ "actions.getTraces": {
72325
+ capName: "actions",
72326
+ capScope: "system",
72327
+ addonId: null,
72328
+ access: "view",
72329
+ role: "admin"
72330
+ },
72331
+ "actions.list": {
72332
+ capName: "actions",
72333
+ capScope: "system",
72334
+ addonId: null,
72335
+ access: "view",
72336
+ role: "admin"
72337
+ },
72338
+ "actions.listForCamera": {
72339
+ capName: "actions",
72340
+ capScope: "system",
72341
+ addonId: null,
72342
+ access: "view",
72343
+ role: "viewer"
72344
+ },
72345
+ "actions.run": {
72346
+ capName: "actions",
72347
+ capScope: "system",
72348
+ addonId: null,
72349
+ access: "create",
72350
+ role: "viewer"
72351
+ },
72352
+ "actions.setLinks": {
72353
+ capName: "actions",
72354
+ capScope: "system",
72355
+ addonId: null,
72356
+ access: "create",
72357
+ role: "admin"
72358
+ },
72359
+ "actions.stop": {
72360
+ capName: "actions",
72361
+ capScope: "system",
72362
+ addonId: null,
72363
+ access: "create",
72364
+ role: "admin"
72365
+ },
72366
+ "actions.update": {
72367
+ capName: "actions",
72368
+ capScope: "system",
72369
+ addonId: null,
72370
+ access: "create",
72371
+ role: "admin"
72372
+ },
72373
+ "actionSource.listActions": {
72374
+ capName: "action-source",
72375
+ capScope: "system",
72376
+ addonId: null,
72377
+ access: "view",
72378
+ role: "admin"
72379
+ },
72380
+ "actionSource.runAction": {
72381
+ capName: "action-source",
72382
+ capScope: "system",
72383
+ addonId: null,
72384
+ access: "create",
72385
+ role: "admin"
72386
+ },
72387
+ "actionSource.stopAction": {
72388
+ capName: "action-source",
72389
+ capScope: "system",
72390
+ addonId: null,
72391
+ access: "create",
72392
+ role: "admin"
72393
+ },
71306
72394
  "addonPages.listPages": {
71307
72395
  capName: "addon-pages",
71308
72396
  capScope: "system",
@@ -72584,6 +73672,13 @@ Object.freeze({
72584
73672
  access: "view",
72585
73673
  role: "admin"
72586
73674
  },
73675
+ "coreBlocks.getAutomationRunState": {
73676
+ capName: "core-blocks",
73677
+ capScope: "system",
73678
+ addonId: null,
73679
+ access: "view",
73680
+ role: "admin"
73681
+ },
72587
73682
  "coreBlocks.getBlockSettings": {
72588
73683
  capName: "core-blocks",
72589
73684
  capScope: "system",
@@ -72605,6 +73700,13 @@ Object.freeze({
72605
73700
  access: "view",
72606
73701
  role: "admin"
72607
73702
  },
73703
+ "coreBlocks.getTraces": {
73704
+ capName: "core-blocks",
73705
+ capScope: "system",
73706
+ addonId: null,
73707
+ access: "view",
73708
+ role: "admin"
73709
+ },
72608
73710
  "coreBlocks.getTypeDefs": {
72609
73711
  capName: "core-blocks",
72610
73712
  capScope: "system",
@@ -72640,6 +73742,13 @@ Object.freeze({
72640
73742
  access: "create",
72641
73743
  role: "admin"
72642
73744
  },
73745
+ "coreBlocks.reportAutomationRun": {
73746
+ capName: "core-blocks",
73747
+ capScope: "system",
73748
+ addonId: null,
73749
+ access: "create",
73750
+ role: "admin"
73751
+ },
72643
73752
  "coreBlocks.restart": {
72644
73753
  capName: "core-blocks",
72645
73754
  capScope: "system",
@@ -74509,6 +75618,13 @@ Object.freeze({
74509
75618
  access: "view",
74510
75619
  role: "viewer"
74511
75620
  },
75621
+ "groupActive.getStatus": {
75622
+ capName: "group-active",
75623
+ capScope: "device",
75624
+ addonId: null,
75625
+ access: "view",
75626
+ role: "viewer"
75627
+ },
74512
75628
  "groupCommands.getStatus": {
74513
75629
  capName: "group-commands",
74514
75630
  capScope: "device",
@@ -75475,6 +76591,13 @@ Object.freeze({
75475
76591
  access: "create",
75476
76592
  role: "admin"
75477
76593
  },
76594
+ "meter.getStatus": {
76595
+ capName: "meter",
76596
+ capScope: "device",
76597
+ addonId: null,
76598
+ access: "view",
76599
+ role: "viewer"
76600
+ },
75478
76601
  "metricsProvider.collectSnapshot": {
75479
76602
  capName: "metrics-provider",
75480
76603
  capScope: "system",
@@ -81199,7 +82322,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81199
82322
  label: t("Full brightness", "Luminosità massima"),
81200
82323
  phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
81201
82324
  icon: "sun",
81202
- args: { percentage: 100 }
82325
+ args: { level: 100 }
81203
82326
  },
81204
82327
  {
81205
82328
  id: "brightness.half",
@@ -81207,7 +82330,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81207
82330
  label: t("Half brightness", "Luminosità a metà"),
81208
82331
  phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
81209
82332
  icon: "sun-dim",
81210
- args: { percentage: 50 }
82333
+ args: { level: 50 }
81211
82334
  },
81212
82335
  {
81213
82336
  id: "brightness.off",
@@ -81215,16 +82338,16 @@ var ACTUATOR_CAP_VOCABULARY = {
81215
82338
  label: t("Brightness off", "Luminosità a zero"),
81216
82339
  phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
81217
82340
  icon: "lightbulb-off",
81218
- args: { percentage: 0 }
82341
+ args: { level: 0 }
81219
82342
  },
81220
82343
  {
81221
82344
  id: "brightness.set",
81222
82345
  method: "setBrightness",
81223
82346
  label: t("Set brightness", "Imposta la luminosità"),
81224
- phrase: t("Set the brightness of {device} to {percentage}", "Imposta la luminosità di {device} a {percentage}"),
82347
+ phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
81225
82348
  icon: "sun-medium",
81226
82349
  openArgs: [{
81227
- key: "percentage",
82350
+ key: "level",
81228
82351
  label: PERCENT,
81229
82352
  offer: {
81230
82353
  kind: "number",
@@ -81238,13 +82361,13 @@ var ACTUATOR_CAP_VOCABULARY = {
81238
82361
  ],
81239
82362
  state: [{
81240
82363
  kind: "numeric",
81241
- field: "percentage",
82364
+ field: "level",
81242
82365
  quantity: t("the brightness", "la luminosità"),
81243
82366
  label: t("Brightness", "Luminosità"),
81244
82367
  unit: "%",
81245
82368
  icon: "sun"
81246
82369
  }],
81247
- events: { onBrightnessChanged: { notOperatorFacing: "the `percentage` state trigger reads the same change" } }
82370
+ events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
81248
82371
  },
81249
82372
  color: {
81250
82373
  scope: "device",
@@ -81258,7 +82381,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81258
82381
  icon: "sun",
81259
82382
  args: { color: {
81260
82383
  mode: "mired",
81261
- mireds: 370
82384
+ colorTemperature: 370
81262
82385
  } }
81263
82386
  },
81264
82387
  {
@@ -81269,7 +82392,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81269
82392
  icon: "snowflake",
81270
82393
  args: { color: {
81271
82394
  mode: "mired",
81272
- mireds: 200
82395
+ colorTemperature: 200
81273
82396
  } }
81274
82397
  },
81275
82398
  {
@@ -81883,7 +83006,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81883
83006
  label: t("Fan off", "Ventilatore spento"),
81884
83007
  phrase: t("Stop {device}", "Ferma {device}"),
81885
83008
  icon: "power-off",
81886
- args: { percentage: 0 }
83009
+ args: { speed: 0 }
81887
83010
  },
81888
83011
  {
81889
83012
  id: "fan.full",
@@ -81891,16 +83014,16 @@ var ACTUATOR_CAP_VOCABULARY = {
81891
83014
  label: t("Fan full", "Ventilatore al massimo"),
81892
83015
  phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
81893
83016
  icon: "fan",
81894
- args: { percentage: 100 }
83017
+ args: { speed: 100 }
81895
83018
  },
81896
83019
  {
81897
83020
  id: "fan.speed",
81898
83021
  method: "setPercentage",
81899
83022
  label: t("Set speed", "Imposta la velocità"),
81900
- phrase: t("Set the speed of {device} to {percentage}", "Imposta la velocità di {device} al {percentage}"),
83023
+ phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
81901
83024
  icon: "fan",
81902
83025
  openArgs: [{
81903
- key: "percentage",
83026
+ key: "speed",
81904
83027
  label: t("Speed", "Velocità"),
81905
83028
  offer: {
81906
83029
  kind: "number",
@@ -81962,7 +83085,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81962
83085
  ],
81963
83086
  state: [{
81964
83087
  kind: "numeric",
81965
- field: "percentage",
83088
+ field: "speed",
81966
83089
  quantity: t("the speed", "la velocità"),
81967
83090
  label: t("Speed", "Velocità"),
81968
83091
  unit: "%",
@@ -82760,7 +83883,7 @@ var APPLIANCE_CAP_VOCABULARY = {
82760
83883
  },
82761
83884
  {
82762
83885
  kind: "numeric",
82763
- field: "progressPercent",
83886
+ field: "progress",
82764
83887
  quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
82765
83888
  label: t("Progress", "Avanzamento"),
82766
83889
  unit: "%",
@@ -83118,10 +84241,10 @@ var APPLIANCE_CAP_VOCABULARY = {
83118
84241
  id: "media.seek",
83119
84242
  method: "seek",
83120
84243
  label: t("Seek", "Vai al punto"),
83121
- phrase: t("Move {device} to {positionMs}", "Porta {device} al punto {positionMs}"),
84244
+ phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
83122
84245
  icon: "fast-forward",
83123
84246
  openArgs: [{
83124
- key: "positionMs",
84247
+ key: "position",
83125
84248
  label: t("Position", "Posizione"),
83126
84249
  offer: {
83127
84250
  kind: "number",
@@ -83573,7 +84696,39 @@ var APPLIANCE_CAP_VOCABULARY = {
83573
84696
  phrase: t("When {device} finishes", "Quando {device} finisce"),
83574
84697
  icon: "square"
83575
84698
  }
83576
- }
84699
+ },
84700
+ overrides: { byType: {
84701
+ [DeviceType.Washer]: {
84702
+ state: { running: {
84703
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
84704
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
84705
+ } },
84706
+ events: {
84707
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
84708
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
84709
+ }
84710
+ },
84711
+ [DeviceType.Dryer]: {
84712
+ state: { running: {
84713
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
84714
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
84715
+ } },
84716
+ events: {
84717
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
84718
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
84719
+ }
84720
+ },
84721
+ [DeviceType.Dishwasher]: {
84722
+ state: { running: {
84723
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
84724
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
84725
+ } },
84726
+ events: {
84727
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
84728
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
84729
+ }
84730
+ }
84731
+ } }
83577
84732
  },
83578
84733
  update: {
83579
84734
  scope: "device",
@@ -84138,7 +85293,7 @@ var SENSOR_CAP_VOCABULARY = {
84138
85293
  state: [
84139
85294
  {
84140
85295
  kind: "numeric",
84141
- field: "co2Ppm",
85296
+ field: "co2",
84142
85297
  quantity: t("the CO₂", "la CO₂"),
84143
85298
  label: t("CO₂", "CO₂"),
84144
85299
  unit: "ppm",
@@ -84146,7 +85301,7 @@ var SENSOR_CAP_VOCABULARY = {
84146
85301
  },
84147
85302
  {
84148
85303
  kind: "numeric",
84149
- field: "vocPpb",
85304
+ field: "voc",
84150
85305
  quantity: t("the VOC level", "il livello di COV"),
84151
85306
  label: t("VOC", "COV"),
84152
85307
  unit: "ppb",
@@ -84182,7 +85337,7 @@ var SENSOR_CAP_VOCABULARY = {
84182
85337
  noun: t("Light level", "Luminosità ambiente"),
84183
85338
  state: [{
84184
85339
  kind: "numeric",
84185
- field: "lux",
85340
+ field: "illuminance",
84186
85341
  quantity: t("the light level", "la luminosità"),
84187
85342
  label: t("Illuminance", "Illuminamento"),
84188
85343
  unit: "lx",
@@ -84345,6 +85500,10 @@ var SENSOR_CAP_VOCABULARY = {
84345
85500
  icon: "flame"
84346
85501
  }]
84347
85502
  },
85503
+ "group-active": {
85504
+ scope: "device",
85505
+ noun: t("Group state", "Stato del gruppo")
85506
+ },
84348
85507
  "group-commands": {
84349
85508
  scope: "device",
84350
85509
  noun: t("Group command", "Comando di gruppo")
@@ -84381,7 +85540,7 @@ var SENSOR_CAP_VOCABULARY = {
84381
85540
  noun: t("Humidity", "Umidità"),
84382
85541
  state: [{
84383
85542
  kind: "numeric",
84384
- field: "percent",
85543
+ field: "humidity",
84385
85544
  quantity: t("the humidity", "l’umidità"),
84386
85545
  label: t("Humidity", "Umidità"),
84387
85546
  unit: "%",
@@ -84401,6 +85560,17 @@ var SENSOR_CAP_VOCABULARY = {
84401
85560
  }],
84402
85561
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
84403
85562
  },
85563
+ meter: {
85564
+ scope: "device",
85565
+ noun: t("Meter", "Contatore"),
85566
+ state: [{
85567
+ kind: "numeric",
85568
+ field: "value",
85569
+ quantity: t("the total", "il totale"),
85570
+ label: t("Total", "Totale"),
85571
+ icon: "gauge"
85572
+ }]
85573
+ },
84404
85574
  "numeric-sensor": {
84405
85575
  scope: "device",
84406
85576
  noun: t("Sensor", "Sensore"),
@@ -84418,7 +85588,7 @@ var SENSOR_CAP_VOCABULARY = {
84418
85588
  state: [
84419
85589
  {
84420
85590
  kind: "numeric",
84421
- field: "watts",
85591
+ field: "power",
84422
85592
  quantity: t("the power draw", "il consumo"),
84423
85593
  label: t("Power", "Potenza"),
84424
85594
  unit: "W",
@@ -84426,7 +85596,7 @@ var SENSOR_CAP_VOCABULARY = {
84426
85596
  },
84427
85597
  {
84428
85598
  kind: "numeric",
84429
- field: "kwhTotal",
85599
+ field: "energy",
84430
85600
  quantity: t("the energy used", "l’energia consumata"),
84431
85601
  label: t("Energy", "Energia"),
84432
85602
  unit: "kWh",
@@ -84434,7 +85604,7 @@ var SENSOR_CAP_VOCABULARY = {
84434
85604
  },
84435
85605
  {
84436
85606
  kind: "numeric",
84437
- field: "volts",
85607
+ field: "voltage",
84438
85608
  quantity: t("the voltage", "la tensione"),
84439
85609
  label: t("Voltage", "Tensione"),
84440
85610
  unit: "V",
@@ -84442,7 +85612,7 @@ var SENSOR_CAP_VOCABULARY = {
84442
85612
  },
84443
85613
  {
84444
85614
  kind: "numeric",
84445
- field: "amps",
85615
+ field: "current",
84446
85616
  quantity: t("the current", "la corrente"),
84447
85617
  label: t("Current", "Corrente"),
84448
85618
  unit: "A",
@@ -84459,13 +85629,6 @@ var SENSOR_CAP_VOCABULARY = {
84459
85629
  label: t("Where", "Posizione"),
84460
85630
  phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
84461
85631
  icon: "map-pin"
84462
- }, {
84463
- kind: "numeric",
84464
- field: "batteryPercent",
84465
- quantity: t("the phone battery", "la batteria del telefono"),
84466
- label: t("Battery", "Batteria"),
84467
- unit: "%",
84468
- icon: "battery"
84469
85632
  }]
84470
85633
  },
84471
85634
  "pressure-sensor": {
@@ -84473,7 +85636,7 @@ var SENSOR_CAP_VOCABULARY = {
84473
85636
  noun: t("Pressure", "Pressione"),
84474
85637
  state: [{
84475
85638
  kind: "numeric",
84476
- field: "hpa",
85639
+ field: "pressure",
84477
85640
  quantity: t("the pressure", "la pressione"),
84478
85641
  label: t("Pressure", "Pressione"),
84479
85642
  unit: "hPa",
@@ -84509,7 +85672,7 @@ var SENSOR_CAP_VOCABULARY = {
84509
85672
  noun: t("Temperature", "Temperatura"),
84510
85673
  state: [{
84511
85674
  kind: "numeric",
84512
- field: "celsius",
85675
+ field: "temperature",
84513
85676
  quantity: t("the temperature", "la temperatura"),
84514
85677
  label: t("Temperature", "Temperatura"),
84515
85678
  unit: "°C",
@@ -84580,7 +85743,7 @@ var SENSOR_CAP_VOCABULARY = {
84580
85743
  state: [
84581
85744
  {
84582
85745
  kind: "numeric",
84583
- field: "percentage",
85746
+ field: "level",
84584
85747
  quantity: t("the battery level", "il livello della batteria"),
84585
85748
  label: t("Battery", "Batteria"),
84586
85749
  unit: "%",
@@ -84622,7 +85785,7 @@ var SENSOR_CAP_VOCABULARY = {
84622
85785
  icon: "battery-charging"
84623
85786
  }
84624
85787
  ],
84625
- events: { onStatusChanged: { notOperatorFacing: "the percentage / sleeping / charging state triggers each read one field of this change" } },
85788
+ events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
84626
85789
  extraEvents: [{
84627
85790
  category: EventCategory.DeviceAwake,
84628
85791
  label: t("Woke up", "Risvegliato"),
@@ -84640,7 +85803,7 @@ var SENSOR_CAP_VOCABULARY = {
84640
85803
  noun: t("Network link", "Collegamento di rete"),
84641
85804
  state: [{
84642
85805
  kind: "numeric",
84643
- field: "signalPercent",
85806
+ field: "signal",
84644
85807
  quantity: t("the signal strength", "l’intensità del segnale"),
84645
85808
  label: t("Signal", "Segnale"),
84646
85809
  unit: "%",
@@ -84733,6 +85896,8 @@ var ML = "model and training machinery — no device action";
84733
85896
  var INTERNAL$1 = "internal plumbing between addons — never operator-facing";
84734
85897
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
84735
85898
  var SYSTEM_CAP_VOCABULARY = {
85899
+ "action-source": system(INTERNAL$1),
85900
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
84736
85901
  "addon-pages": system(UI_PLUMBING),
84737
85902
  "addon-pages-source": system(UI_PLUMBING),
84738
85903
  "addon-routes": system(UI_PLUMBING),
@@ -84873,8 +86038,47 @@ function projectAction(cap, action) {
84873
86038
  };
84874
86039
  }
84875
86040
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
84876
- 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;
86041
+ 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;
84877
86042
  DeviceFeature.BatteryOperated;
86043
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
86044
+ 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.";
86045
+ 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.";
86046
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
86047
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
86048
+ 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.";
86049
+ function withheld(reason) {
86050
+ return {
86051
+ [DeviceType.Washer]: {
86052
+ exported: false,
86053
+ reason
86054
+ },
86055
+ [DeviceType.Dryer]: {
86056
+ exported: false,
86057
+ reason
86058
+ },
86059
+ [DeviceType.Dishwasher]: {
86060
+ exported: false,
86061
+ reason
86062
+ }
86063
+ };
86064
+ }
86065
+ function offered(received) {
86066
+ return {
86067
+ [DeviceType.Washer]: {
86068
+ exported: true,
86069
+ as: received
86070
+ },
86071
+ [DeviceType.Dryer]: {
86072
+ exported: true,
86073
+ as: received
86074
+ },
86075
+ [DeviceType.Dishwasher]: {
86076
+ exported: true,
86077
+ as: received
86078
+ }
86079
+ };
86080
+ }
86081
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
84878
86082
  Object.freeze({
84879
86083
  "accessories.getStatus": [{
84880
86084
  name: "deviceId",
@@ -84890,6 +86094,31 @@ Object.freeze({
84890
86094
  form: "single",
84891
86095
  optional: false
84892
86096
  }],
86097
+ "actions.get": [{
86098
+ name: "deviceId",
86099
+ form: "single",
86100
+ optional: true
86101
+ }],
86102
+ "actions.getLinks": [{
86103
+ name: "deviceId",
86104
+ form: "single",
86105
+ optional: false
86106
+ }],
86107
+ "actions.listForCamera": [{
86108
+ name: "deviceId",
86109
+ form: "single",
86110
+ optional: false
86111
+ }],
86112
+ "actions.run": [{
86113
+ name: "deviceId",
86114
+ form: "single",
86115
+ optional: true
86116
+ }],
86117
+ "actions.setLinks": [{
86118
+ name: "deviceId",
86119
+ form: "single",
86120
+ optional: false
86121
+ }],
84893
86122
  "addonSettings.getDeviceSettings": [{
84894
86123
  name: "deviceId",
84895
86124
  form: "single",
@@ -85841,6 +87070,11 @@ Object.freeze({
85841
87070
  form: "single",
85842
87071
  optional: false
85843
87072
  }],
87073
+ "groupActive.getStatus": [{
87074
+ name: "deviceId",
87075
+ form: "single",
87076
+ optional: false
87077
+ }],
85844
87078
  "groupCommands.getStatus": [{
85845
87079
  name: "deviceId",
85846
87080
  form: "single",
@@ -86051,6 +87285,11 @@ Object.freeze({
86051
87285
  form: "single",
86052
87286
  optional: false
86053
87287
  }],
87288
+ "meter.getStatus": [{
87289
+ name: "deviceId",
87290
+ form: "single",
87291
+ optional: false
87292
+ }],
86054
87293
  "motion.getStatus": [{
86055
87294
  name: "deviceId",
86056
87295
  form: "single",
@@ -87658,7 +88897,6 @@ Object.freeze({
87658
88897
  "network-access": "ingress",
87659
88898
  "smtp-provider": "email"
87660
88899
  });
87661
- DeviceType.Camera;
87662
88900
  /**
87663
88901
  * A saved Customize assignment (D936). The schema is the type: callers use
87664
88902
  * `z.infer`, they do not restate the shape.
@@ -88962,6 +90200,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
88962
90200
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
88963
90201
  DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
88964
90202
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
90203
+ String.raw`(?<=^|["'(\s>,\[{])`;
88965
90204
  var MB = 1024 * 1024;
88966
90205
  1024 * MB, 3072 * MB;
88967
90206
  //#endregion
@@ -197745,9 +198984,9 @@ function decodeMatterAttribute(input) {
197745
198984
  case MatterClusterId.LevelControl: return decodeLevel(attributeId, value);
197746
198985
  case MatterClusterId.BooleanState: return decodeContact(attributeId, value);
197747
198986
  case MatterClusterId.OccupancySensing: return decodeOccupancy(attributeId, value);
197748
- case MatterClusterId.TemperatureMeasurement: return decodeScaledMeasurement(attributeId, value, "temperature-sensor", "celsius", .01);
197749
- case MatterClusterId.RelativeHumidityMeasurement: return decodeScaledMeasurement(attributeId, value, "humidity-sensor", "percent", .01);
197750
- case MatterClusterId.PressureMeasurement: return decodeScaledMeasurement(attributeId, value, "pressure-sensor", "hpa", 1);
198987
+ case MatterClusterId.TemperatureMeasurement: return decodeScaledMeasurement(attributeId, value, "temperature-sensor", "temperature", .01);
198988
+ case MatterClusterId.RelativeHumidityMeasurement: return decodeScaledMeasurement(attributeId, value, "humidity-sensor", "humidity", .01);
198989
+ case MatterClusterId.PressureMeasurement: return decodeScaledMeasurement(attributeId, value, "pressure-sensor", "pressure", 1);
197751
198990
  case MatterClusterId.IlluminanceMeasurement: return decodeIlluminance(attributeId, value);
197752
198991
  case MatterClusterId.PowerSource: return decodeBattery(attributeId, value);
197753
198992
  case MatterClusterId.Thermostat: return decodeThermostat(attributeId, value);
@@ -197842,7 +199081,7 @@ function decodeLevel(attributeId, value) {
197842
199081
  return {
197843
199082
  cap: "brightness",
197844
199083
  patch: {
197845
- percentage: Math.round(Math.max(0, Math.min(254, level)) / 254 * 100),
199084
+ level: Math.round(Math.max(0, Math.min(254, level)) / 254 * 100),
197846
199085
  lastChangedAt: Date.now()
197847
199086
  }
197848
199087
  };
@@ -197891,7 +199130,7 @@ function decodeIlluminance(attributeId, value) {
197891
199130
  return {
197892
199131
  cap: "ambient-light-sensor",
197893
199132
  patch: {
197894
- lux: Math.round(10 ** ((raw - 1) / 1e4)),
199133
+ illuminance: Math.round(10 ** ((raw - 1) / 1e4)),
197895
199134
  lastFetchedAt: Date.now()
197896
199135
  }
197897
199136
  };
@@ -197903,7 +199142,7 @@ function decodeBattery(attributeId, value) {
197903
199142
  return {
197904
199143
  cap: "battery",
197905
199144
  patch: {
197906
- percentage: Math.max(0, Math.min(100, Math.round(half / 2))),
199145
+ level: Math.max(0, Math.min(100, Math.round(half / 2))),
197907
199146
  lastUpdated: Date.now()
197908
199147
  }
197909
199148
  };
@@ -197966,7 +199205,7 @@ function decodeElectricalPower(attributeId, value) {
197966
199205
  return mw === null ? null : {
197967
199206
  cap: "power-meter",
197968
199207
  patch: {
197969
- watts: mw / 1e3,
199208
+ power: mw / 1e3,
197970
199209
  lastFetchedAt: Date.now()
197971
199210
  }
197972
199211
  };
@@ -197976,7 +199215,7 @@ function decodeElectricalPower(attributeId, value) {
197976
199215
  return mv === null ? null : {
197977
199216
  cap: "power-meter",
197978
199217
  patch: {
197979
- volts: mv / 1e3,
199218
+ voltage: mv / 1e3,
197980
199219
  lastFetchedAt: Date.now()
197981
199220
  }
197982
199221
  };
@@ -197986,7 +199225,7 @@ function decodeElectricalPower(attributeId, value) {
197986
199225
  return ma === null ? null : {
197987
199226
  cap: "power-meter",
197988
199227
  patch: {
197989
- amps: ma / 1e3,
199228
+ current: ma / 1e3,
197990
199229
  lastFetchedAt: Date.now()
197991
199230
  }
197992
199231
  };
@@ -198001,7 +199240,7 @@ function decodeElectricalEnergy(attributeId, value) {
198001
199240
  return {
198002
199241
  cap: "power-meter",
198003
199242
  patch: {
198004
- kwhTotal: mwh / 1e6,
199243
+ energy: mwh / 1e6,
198005
199244
  lastFetchedAt: Date.now()
198006
199245
  }
198007
199246
  };
@@ -200225,16 +201464,16 @@ var MatterEndpointDevice = class extends BaseDevice {
200225
201464
  }
200226
201465
  registerBrightness() {
200227
201466
  const COLD = {
200228
- percentage: 0,
201467
+ level: 0,
200229
201468
  lastChangedAt: 0
200230
201469
  };
200231
201470
  this.ctx.registerNativeCap(brightnessCapability, {
200232
201471
  getStatus: async () => this.runtimeState.getCapState("brightness") ?? COLD,
200233
- setBrightness: async ({ percentage }) => {
201472
+ setBrightness: async ({ level: percentage }) => {
200234
201473
  const level = Math.round(Math.max(0, Math.min(100, percentage)) / 100 * 254);
200235
201474
  await this.invoke(MatterClusterId.LevelControl, "moveToLevel", { level });
200236
201475
  this.runtimeState.setCapState("brightness", {
200237
- percentage,
201476
+ level: percentage,
200238
201477
  lastChangedAt: Date.now()
200239
201478
  });
200240
201479
  }
@@ -200445,7 +201684,7 @@ var MatterEndpointDevice = class extends BaseDevice {
200445
201684
  }
200446
201685
  registerTemperatureSensor() {
200447
201686
  const COLD = {
200448
- celsius: 0,
201687
+ temperature: 0,
200449
201688
  lastFetchedAt: 0
200450
201689
  };
200451
201690
  this.ctx.registerNativeCap(temperatureSensorCapability, { getStatus: async () => this.runtimeState.getCapState("temperature-sensor") ?? COLD });
@@ -200453,7 +201692,7 @@ var MatterEndpointDevice = class extends BaseDevice {
200453
201692
  }
200454
201693
  registerHumiditySensor() {
200455
201694
  const COLD = {
200456
- percent: 0,
201695
+ humidity: 0,
200457
201696
  lastFetchedAt: 0
200458
201697
  };
200459
201698
  this.ctx.registerNativeCap(humiditySensorCapability, { getStatus: async () => this.runtimeState.getCapState("humidity-sensor") ?? COLD });
@@ -200461,7 +201700,7 @@ var MatterEndpointDevice = class extends BaseDevice {
200461
201700
  }
200462
201701
  registerPressureSensor() {
200463
201702
  const COLD = {
200464
- hpa: 0,
201703
+ pressure: 0,
200465
201704
  lastFetchedAt: 0
200466
201705
  };
200467
201706
  this.ctx.registerNativeCap(pressureSensorCapability, { getStatus: async () => this.runtimeState.getCapState("pressure-sensor") ?? COLD });
@@ -200469,7 +201708,7 @@ var MatterEndpointDevice = class extends BaseDevice {
200469
201708
  }
200470
201709
  registerAmbientLightSensor() {
200471
201710
  const COLD = {
200472
- lux: 0,
201711
+ illuminance: 0,
200473
201712
  lastFetchedAt: 0
200474
201713
  };
200475
201714
  this.ctx.registerNativeCap(ambientLightSensorCapability, { getStatus: async () => this.runtimeState.getCapState("ambient-light-sensor") ?? COLD });
@@ -200477,7 +201716,7 @@ var MatterEndpointDevice = class extends BaseDevice {
200477
201716
  }
200478
201717
  registerBattery() {
200479
201718
  const COLD = {
200480
- percentage: 0,
201719
+ level: 0,
200481
201720
  charging: "unknown",
200482
201721
  sleeping: false,
200483
201722
  lastUpdated: 0