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