@camstack/addon-provider-homematic 1.2.159 → 1.2.161

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
@@ -9,7 +9,7 @@ import { join } from "path";
9
9
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
10
10
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
11
11
  //#endregion
12
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
12
+ //#region ../types/dist/event-category-DmZDPDf_.mjs
13
13
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
14
14
  EventCategory["SystemBoot"] = "system.boot";
15
15
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -636,7 +636,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
636
636
  * (a track was promoted to a stationary-registry entry) or departs (the
637
637
  * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
638
638
  * the dedicated "Stationary" section — never the live event feed. Payload:
639
- * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
639
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
640
640
  */
641
641
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
642
642
  /**
@@ -774,6 +774,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
774
774
  */
775
775
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
776
776
  /**
777
+ * An action run started or ended. Emitted by the `actions` surface.
778
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
779
+ */
780
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
781
+ /**
777
782
  * Progress update from a running model conversion job.
778
783
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
779
784
  * Emitted by `addon-model-studio` on the converting node.
@@ -5585,7 +5590,7 @@ var ZodIssueCode = {
5585
5590
  var ZodFirstPartyTypeKind;
5586
5591
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5587
5592
  //#endregion
5588
- //#region ../types/dist/sleep-bykKrTgu.mjs
5593
+ //#region ../types/dist/sleep-Crlmu_tO.mjs
5589
5594
  /**
5590
5595
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5591
5596
  * window to float samples (D455).
@@ -8893,6 +8898,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
8893
8898
  /** Every capability name, so addon code can check a name without the schema graph. */
8894
8899
  var CAP_NAMES = Object.freeze([
8895
8900
  "accessories",
8901
+ "action-source",
8902
+ "actions",
8896
8903
  "addon-pages",
8897
8904
  "addon-pages-source",
8898
8905
  "addon-routes",
@@ -8966,6 +8973,7 @@ var CAP_NAMES = Object.freeze([
8966
8973
  "filesystem-browse",
8967
8974
  "flood",
8968
8975
  "gas",
8976
+ "group-active",
8969
8977
  "group-commands",
8970
8978
  "group-status",
8971
8979
  "heating-valve",
@@ -8988,6 +8996,7 @@ var CAP_NAMES = Object.freeze([
8988
8996
  "login-method",
8989
8997
  "media-player",
8990
8998
  "mesh-network",
8999
+ "meter",
8991
9000
  "metrics-provider",
8992
9001
  "model-convert",
8993
9002
  "model-distributor",
@@ -9079,11 +9088,11 @@ var CAP_NAMES = Object.freeze([
9079
9088
  Object.freeze({
9080
9089
  "air-quality-sensor": [
9081
9090
  {
9082
- "path": "co2Ppm",
9091
+ "path": "co2",
9083
9092
  "valueType": "number"
9084
9093
  },
9085
9094
  {
9086
- "path": "vocPpb",
9095
+ "path": "voc",
9087
9096
  "valueType": "number"
9088
9097
  },
9089
9098
  {
@@ -9133,7 +9142,7 @@ Object.freeze({
9133
9142
  }
9134
9143
  ],
9135
9144
  "ambient-light-sensor": [{
9136
- "path": "lux",
9145
+ "path": "illuminance",
9137
9146
  "valueType": "number"
9138
9147
  }],
9139
9148
  "audio-metrics": [
@@ -9194,7 +9203,7 @@ Object.freeze({
9194
9203
  ],
9195
9204
  "battery": [
9196
9205
  {
9197
- "path": "percentage",
9206
+ "path": "level",
9198
9207
  "valueType": "number"
9199
9208
  },
9200
9209
  {
@@ -9216,7 +9225,7 @@ Object.freeze({
9216
9225
  "valueType": "number"
9217
9226
  },
9218
9227
  {
9219
- "path": "volts",
9228
+ "path": "voltage",
9220
9229
  "valueType": "number"
9221
9230
  },
9222
9231
  {
@@ -9237,7 +9246,7 @@ Object.freeze({
9237
9246
  "valueType": "boolean"
9238
9247
  }],
9239
9248
  "brightness": [{
9240
- "path": "percentage",
9249
+ "path": "level",
9241
9250
  "valueType": "number"
9242
9251
  }],
9243
9252
  "broker": [{
@@ -9438,7 +9447,7 @@ Object.freeze({
9438
9447
  "valueType": "number"
9439
9448
  },
9440
9449
  {
9441
- "path": "mireds",
9450
+ "path": "colorTemperature",
9442
9451
  "valueType": "number"
9443
9452
  }
9444
9453
  ],
@@ -9617,15 +9626,15 @@ Object.freeze({
9617
9626
  "valueType": "string"
9618
9627
  },
9619
9628
  {
9620
- "path": "remainingSeconds",
9629
+ "path": "remaining",
9621
9630
  "valueType": "number"
9622
9631
  },
9623
9632
  {
9624
- "path": "delayedStartSeconds",
9633
+ "path": "delayedStart",
9625
9634
  "valueType": "number"
9626
9635
  },
9627
9636
  {
9628
- "path": "progressPercent",
9637
+ "path": "progress",
9629
9638
  "valueType": "number"
9630
9639
  },
9631
9640
  {
@@ -9739,7 +9748,7 @@ Object.freeze({
9739
9748
  ],
9740
9749
  "fan-control": [
9741
9750
  {
9742
- "path": "percentage",
9751
+ "path": "speed",
9743
9752
  "valueType": "number"
9744
9753
  },
9745
9754
  {
@@ -9786,6 +9795,10 @@ Object.freeze({
9786
9795
  "path": "detected",
9787
9796
  "valueType": "boolean"
9788
9797
  }],
9798
+ "group-active": [{
9799
+ "path": "active",
9800
+ "valueType": "boolean"
9801
+ }],
9789
9802
  "group-status": [
9790
9803
  {
9791
9804
  "path": "included",
@@ -9835,7 +9848,7 @@ Object.freeze({
9835
9848
  }
9836
9849
  ],
9837
9850
  "humidity-sensor": [{
9838
- "path": "percent",
9851
+ "path": "humidity",
9839
9852
  "valueType": "number"
9840
9853
  }],
9841
9854
  "image": [{
@@ -9945,7 +9958,7 @@ Object.freeze({
9945
9958
  "valueType": "number"
9946
9959
  },
9947
9960
  {
9948
- "path": "progressPercent",
9961
+ "path": "progress",
9949
9962
  "valueType": "number"
9950
9963
  },
9951
9964
  {
@@ -10024,11 +10037,11 @@ Object.freeze({
10024
10037
  "valueType": "string"
10025
10038
  },
10026
10039
  {
10027
- "path": "positionMs",
10040
+ "path": "position",
10028
10041
  "valueType": "number"
10029
10042
  },
10030
10043
  {
10031
- "path": "durationMs",
10044
+ "path": "duration",
10032
10045
  "valueType": "number"
10033
10046
  },
10034
10047
  {
@@ -10045,6 +10058,10 @@ Object.freeze({
10045
10058
  ]
10046
10059
  }
10047
10060
  ],
10061
+ "meter": [{
10062
+ "path": "value",
10063
+ "valueType": "number"
10064
+ }],
10048
10065
  "motion": [
10049
10066
  {
10050
10067
  "path": "detected",
@@ -10126,11 +10143,11 @@ Object.freeze({
10126
10143
  ]
10127
10144
  },
10128
10145
  {
10129
- "path": "signalPercent",
10146
+ "path": "signal",
10130
10147
  "valueType": "number"
10131
10148
  },
10132
10149
  {
10133
- "path": "rssiDbm",
10150
+ "path": "rssi",
10134
10151
  "valueType": "number"
10135
10152
  },
10136
10153
  {
@@ -10185,6 +10202,18 @@ Object.freeze({
10185
10202
  "path": "foodLevel",
10186
10203
  "valueType": "number"
10187
10204
  },
10205
+ {
10206
+ "path": "bowlFood",
10207
+ "valueType": "number"
10208
+ },
10209
+ {
10210
+ "path": "eating",
10211
+ "valueType": "boolean"
10212
+ },
10213
+ {
10214
+ "path": "supportsMarkRefilled",
10215
+ "valueType": "boolean"
10216
+ },
10188
10217
  {
10189
10218
  "path": "food1",
10190
10219
  "valueType": "number"
@@ -10249,23 +10278,23 @@ Object.freeze({
10249
10278
  ],
10250
10279
  "power-meter": [
10251
10280
  {
10252
- "path": "watts",
10281
+ "path": "power",
10253
10282
  "valueType": "number"
10254
10283
  },
10255
10284
  {
10256
- "path": "kwhTotal",
10285
+ "path": "energy",
10257
10286
  "valueType": "number"
10258
10287
  },
10259
10288
  {
10260
- "path": "volts",
10289
+ "path": "voltage",
10261
10290
  "valueType": "number"
10262
10291
  },
10263
10292
  {
10264
- "path": "amps",
10293
+ "path": "current",
10265
10294
  "valueType": "number"
10266
10295
  },
10267
10296
  {
10268
- "path": "va",
10297
+ "path": "apparentPower",
10269
10298
  "valueType": "number"
10270
10299
  },
10271
10300
  {
@@ -10293,14 +10322,10 @@ Object.freeze({
10293
10322
  {
10294
10323
  "path": "gps.accuracyMeters",
10295
10324
  "valueType": "number"
10296
- },
10297
- {
10298
- "path": "batteryPercent",
10299
- "valueType": "number"
10300
10325
  }
10301
10326
  ],
10302
10327
  "pressure-sensor": [{
10303
- "path": "hpa",
10328
+ "path": "pressure",
10304
10329
  "valueType": "number"
10305
10330
  }],
10306
10331
  "privacy-mask": [{
@@ -10614,7 +10639,7 @@ Object.freeze({
10614
10639
  "valueType": "boolean"
10615
10640
  }],
10616
10641
  "temperature-sensor": [{
10617
- "path": "celsius",
10642
+ "path": "temperature",
10618
10643
  "valueType": "number"
10619
10644
  }],
10620
10645
  "update": [
@@ -10662,7 +10687,7 @@ Object.freeze({
10662
10687
  "valueType": "string"
10663
10688
  },
10664
10689
  {
10665
- "path": "progressPercent",
10690
+ "path": "progress",
10666
10691
  "valueType": "number"
10667
10692
  },
10668
10693
  {
@@ -10788,7 +10813,7 @@ object({
10788
10813
  params: record(string(), union([string(), number()])).optional()
10789
10814
  });
10790
10815
  //#endregion
10791
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10816
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10792
10817
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10793
10818
  DeviceType["Camera"] = "camera";
10794
10819
  DeviceType["Hub"] = "hub";
@@ -10882,8 +10907,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10882
10907
  DeviceType["PetFeeder"] = "pet-feeder";
10883
10908
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10884
10909
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10885
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10910
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10911
+ * A plug-built dishwasher carries running-state and power instead. */
10886
10912
  DeviceType["Dishwasher"] = "dishwasher";
10913
+ /** Washing machine. A plug-built one is running while its power stays
10914
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10915
+ DeviceType["Washer"] = "washer";
10916
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10917
+ DeviceType["Dryer"] = "dryer";
10887
10918
  return DeviceType;
10888
10919
  }({});
10889
10920
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11141,7 +11172,7 @@ function errMsg(err) {
11141
11172
  return String(err);
11142
11173
  }
11143
11174
  //#endregion
11144
- //#region ../types/dist/composition-OhpuespA.mjs
11175
+ //#region ../types/dist/composition-CZImLpOI.mjs
11145
11176
  /**
11146
11177
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11147
11178
  *
@@ -11456,6 +11487,13 @@ var table = {
11456
11487
  apply: () => {
11457
11488
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11458
11489
  }
11490
+ },
11491
+ schmitt: {
11492
+ minArgs: 3,
11493
+ maxArgs: 5,
11494
+ apply: () => {
11495
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11496
+ }
11459
11497
  }
11460
11498
  };
11461
11499
  Object.freeze(Object.assign(Object.create(null), table));
@@ -11897,6 +11935,99 @@ function validateExpressionSource(src) {
11897
11935
  return null;
11898
11936
  }
11899
11937
  /**
11938
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11939
+ *
11940
+ * Standby of the control board is under 5 W; a motor or a heater is far
11941
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11942
+ * a cycle, so running starts only after 30 s above the line.
11943
+ *
11944
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11945
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11946
+ * report "finished" in the middle of the program, so the stop dwell is
11947
+ * 10 minutes on every type. The number is stored per type so one machine
11948
+ * can move later without moving the others.
11949
+ */
11950
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11951
+ var APPLIANCE_KINDS = [
11952
+ "washer",
11953
+ "dryer",
11954
+ "dishwasher"
11955
+ ];
11956
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11957
+ /**
11958
+ * Statistics and integrals over a history read (D944).
11959
+ *
11960
+ * Pure: the same points always give the same answer, so a restart recomputes
11961
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11962
+ * never the mean of the samples. A hole with no held value is not integrated
11963
+ * as zero. A truncated read is not published.
11964
+ */
11965
+ var STATISTIC_STATS = [
11966
+ "min",
11967
+ "max",
11968
+ "mean",
11969
+ "sum",
11970
+ "count",
11971
+ "change"
11972
+ ];
11973
+ /**
11974
+ * The range a statistics or integral helper asks history for (D944).
11975
+ *
11976
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11977
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11978
+ * is strictly longer than the longest that range can be. Forever qualifies.
11979
+ * Equal to a tier's retention is a refusal, not a short answer.
11980
+ */
11981
+ var MINUTE_MS = 6e4;
11982
+ var HOUR_MS = 60 * MINUTE_MS;
11983
+ var DAY_MS = 24 * HOUR_MS;
11984
+ var ROLLING_SPANS = [
11985
+ "1h",
11986
+ "24h",
11987
+ "2d",
11988
+ "7d",
11989
+ "30d",
11990
+ "custom"
11991
+ ];
11992
+ var CALENDAR_PERIODS = [
11993
+ "today",
11994
+ "yesterday",
11995
+ "this-week",
11996
+ "this-month"
11997
+ ];
11998
+ var RESET_PERIODS = [
11999
+ "never",
12000
+ "daily",
12001
+ "weekly",
12002
+ "monthly"
12003
+ ];
12004
+ 5 * MINUTE_MS;
12005
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
12006
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
12007
+ var rollingWindowObject = object({
12008
+ kind: literal("rolling"),
12009
+ span: _enum(ROLLING_SPANS),
12010
+ customMinutes: number().int().min(5).optional()
12011
+ }).check((payload) => {
12012
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
12013
+ code: "custom",
12014
+ message: "a custom range needs a duration in minutes",
12015
+ path: ["customMinutes"],
12016
+ input: payload.value
12017
+ });
12018
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
12019
+ code: "custom",
12020
+ message: "only a custom range carries a duration",
12021
+ path: ["customMinutes"],
12022
+ input: payload.value
12023
+ });
12024
+ });
12025
+ var RollingWindowSchema = rollingWindowObject;
12026
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
12027
+ kind: literal("calendar"),
12028
+ period: _enum(CALENDAR_PERIODS)
12029
+ })]);
12030
+ /**
11900
12031
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11901
12032
  * id. Split out so helper schemas can name it without a cycle through
11902
12033
  * `composition.ts` (D659).
@@ -11922,7 +12053,13 @@ var GROUP_TEMPLATE_IDS = [
11922
12053
  "occupancy",
11923
12054
  "binary",
11924
12055
  "switch",
11925
- "light"
12056
+ "light",
12057
+ "fan",
12058
+ "cover",
12059
+ "valve",
12060
+ "lock",
12061
+ "media-player",
12062
+ "climate"
11926
12063
  ];
11927
12064
  var NUMERIC_AGGREGATES = [
11928
12065
  "mean",
@@ -11952,14 +12089,73 @@ var HelperMemberSchema = object({
11952
12089
  cap: string().min(1),
11953
12090
  fieldPath: string().min(1)
11954
12091
  });
11955
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11956
- kind: literal("group"),
11957
- templateId: GroupTemplateIdSchema,
11958
- aggregate: AggregateReducerSchema,
11959
- members: array(HelperMemberSchema).min(1).max(64),
11960
- /** Actuator groups only. Readings leave this unset. */
11961
- active: ActiveCriterionSchema.optional()
11962
- })]);
12092
+ var HelperSpecSchema = discriminatedUnion("kind", [
12093
+ object({
12094
+ kind: literal("group"),
12095
+ templateId: GroupTemplateIdSchema,
12096
+ aggregate: AggregateReducerSchema,
12097
+ members: array(HelperMemberSchema).min(1).max(64),
12098
+ /** Actuator groups only. Readings leave this unset. */
12099
+ active: ActiveCriterionSchema.optional()
12100
+ }),
12101
+ object({
12102
+ kind: literal("appliance"),
12103
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
12104
+ appliance: _enum(APPLIANCE_KINDS),
12105
+ thresholdWatts: number().positive(),
12106
+ startDwellMs: number().int().nonnegative(),
12107
+ stopDwellMs: number().int().nonnegative(),
12108
+ member: HelperMemberSchema
12109
+ }),
12110
+ object({
12111
+ kind: literal("statistic"),
12112
+ stat: _enum(STATISTIC_STATS),
12113
+ window: StatisticWindowSchema,
12114
+ source: HelperMemberSchema
12115
+ }),
12116
+ object({
12117
+ kind: literal("integral"),
12118
+ reset: _enum(RESET_PERIODS),
12119
+ output: _enum(["energy", "meter"]),
12120
+ source: HelperMemberSchema
12121
+ }),
12122
+ object({
12123
+ kind: literal("threshold"),
12124
+ source: HelperMemberSchema,
12125
+ high: number(),
12126
+ low: number(),
12127
+ onForMs: number().int().nonnegative(),
12128
+ offForMs: number().int().nonnegative()
12129
+ }),
12130
+ object({
12131
+ kind: literal("arithmetic"),
12132
+ op: _enum([
12133
+ "sum",
12134
+ "difference",
12135
+ "ratio"
12136
+ ]),
12137
+ inputs: array(HelperMemberSchema).min(2).max(64)
12138
+ }),
12139
+ object({
12140
+ kind: literal("expression"),
12141
+ expr: string().min(1),
12142
+ inputs: array(object({
12143
+ name: string().min(1),
12144
+ member: HelperMemberSchema
12145
+ })).min(1).max(64),
12146
+ output: _enum(["number", "boolean"])
12147
+ }),
12148
+ object({
12149
+ kind: literal("derivative"),
12150
+ window: RollingWindowSchema,
12151
+ per: _enum([
12152
+ "second",
12153
+ "minute",
12154
+ "hour"
12155
+ ]),
12156
+ source: HelperMemberSchema
12157
+ })
12158
+ ]);
11963
12159
  /** A composed device's stableId is this prefix plus the block id. */
11964
12160
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11965
12161
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -12020,6 +12216,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12020
12216
  "all"
12021
12217
  ]),
12022
12218
  reads: array(CompositionFieldReadSchema).min(1).max(64),
12219
+ /**
12220
+ * Set when the reads are an enum state, not a yes/no: the listed states
12221
+ * count as active (a cover is `open`). A state outside the list is inactive;
12222
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
12223
+ */
12224
+ activeWhen: array(string().min(1)).min(1).optional(),
12023
12225
  /** `value` is the reading; the others are the member counts. */
12024
12226
  publish: _enum([
12025
12227
  "value",
@@ -12033,6 +12235,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12033
12235
  "omit",
12034
12236
  "offline"
12035
12237
  ])
12238
+ }),
12239
+ object({
12240
+ kind: literal("series"),
12241
+ read: CompositionFieldReadSchema,
12242
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12243
+ publish: _enum([
12244
+ "value",
12245
+ "included",
12246
+ "excluded",
12247
+ "total"
12248
+ ]),
12249
+ /**
12250
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12251
+ * device unseeded until history answers, so a restart cannot publish 0.
12252
+ */
12253
+ empty: _enum([
12254
+ "null",
12255
+ "omit",
12256
+ "offline"
12257
+ ])
12036
12258
  })
12037
12259
  ]);
12038
12260
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -15470,6 +15692,14 @@ var AutomationConditionLeafSchema = object({
15470
15692
  array(union([string(), number()]))
15471
15693
  ])
15472
15694
  });
15695
+ var AutomationConditionStationaryPresentSchema = object({
15696
+ kind: literal("stationary-present"),
15697
+ deviceId: number().int().nonnegative(),
15698
+ /** Class names; a macro class matches every raw class that maps to it. */
15699
+ classes: array(string().min(1)).min(1).optional(),
15700
+ /** The parked object must be in at least one of these zones. */
15701
+ zones: array(string().min(1)).min(1).optional()
15702
+ });
15473
15703
  /**
15474
15704
  * The expression leaf, declared as a plain object rather than an intersection
15475
15705
  * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
@@ -15503,7 +15733,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
15503
15733
  child: AutomationConditionSchema
15504
15734
  }),
15505
15735
  AutomationConditionLeafSchema,
15506
- AutomationConditionExpressionSchema
15736
+ AutomationConditionExpressionSchema,
15737
+ AutomationConditionStationaryPresentSchema
15507
15738
  ]));
15508
15739
  /**
15509
15740
  * What starts a run.
@@ -15544,7 +15775,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15544
15775
  kind: literal("schedule"),
15545
15776
  cron: string().min(1).max(120)
15546
15777
  }),
15547
- object({ kind: literal("manual") })
15778
+ object({ kind: literal("manual") }),
15779
+ object({
15780
+ kind: literal("detection"),
15781
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
15782
+ * matches every raw class that maps to it; any other name matches
15783
+ * exactly. */
15784
+ classes: array(string().min(1)).min(1),
15785
+ deviceIds: array(number().int().nonnegative()).min(1),
15786
+ /** The track must have visited at least one of these zones. */
15787
+ zones: array(string().min(1)).min(1).optional(),
15788
+ /** Best-so-far confidence the track must reach. */
15789
+ minScore: number().min(0).max(1).optional()
15790
+ }),
15791
+ object({
15792
+ kind: literal("stationary"),
15793
+ deviceIds: array(number().int().nonnegative()).min(1),
15794
+ /** Class names; a macro class matches like the detection trigger. */
15795
+ classes: array(string().min(1)).min(1).optional(),
15796
+ /** The parked object must be in at least one of these zones. */
15797
+ zones: array(string().min(1)).min(1).optional(),
15798
+ /** `appeared`: it became parked. `departed`: the registry retired it.
15799
+ * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
15800
+ phase: _enum(["appeared", "departed"])
15801
+ }),
15802
+ object({
15803
+ kind: literal("interval"),
15804
+ minutes: number().int().min(1).max(1440)
15805
+ })
15548
15806
  ]);
15549
15807
  /**
15550
15808
  * One action step.
@@ -15564,28 +15822,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15564
15822
  * is called. The runner still receives a number and is untouched; the
15565
15823
  * resolution is the recipe's job, not the runner's.
15566
15824
  */
15825
+ var AutomationWaitStepSchema = object({
15826
+ kind: literal("wait"),
15827
+ seconds: number().min(0).max(300)
15828
+ });
15829
+ var AutomationCapStepSchema = object({
15830
+ kind: literal("cap"),
15831
+ /**
15832
+ * One device, several same-family devices (locks / climate family / …),
15833
+ * or `{ $var }` for the device that triggered the run. An array is
15834
+ * fan-out at codegen — each id gets the same cap.method call.
15835
+ */
15836
+ deviceId: union([
15837
+ number().int(),
15838
+ array(number().int()).min(1),
15839
+ object({ $var: string().min(1) })
15840
+ ]),
15841
+ cap: string().min(1),
15842
+ method: string().min(1),
15843
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
15844
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
15845
+ * the expression leaf. */
15846
+ args: record(string(), unknown()).optional()
15847
+ });
15848
+ /**
15849
+ * The steps an internal Action may contain: cap and wait, and nothing that can
15850
+ * call back into an Action — so an Action can never reach itself (no cycles).
15851
+ */
15852
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
15567
15853
  var AutomationActionSchema = discriminatedUnion("kind", [
15854
+ AutomationWaitStepSchema,
15855
+ AutomationCapStepSchema,
15568
15856
  object({
15569
- kind: literal("wait"),
15570
- seconds: number().min(0).max(300)
15571
- }),
15572
- object({
15573
- kind: literal("cap"),
15574
- /**
15575
- * One device, several same-family devices (locks / climate family / …),
15576
- * or `{ $var }` for the device that triggered the run. An array is
15577
- * fan-out at codegen — each id gets the same cap.method call.
15578
- */
15579
- deviceId: union([
15580
- number().int(),
15581
- array(number().int()).min(1),
15582
- object({ $var: string().min(1) })
15583
- ]),
15584
- cap: string().min(1),
15585
- method: string().min(1),
15586
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
15587
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
15588
- args: record(string(), unknown()).optional()
15857
+ kind: literal("action"),
15858
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
15859
+ actionId: string().min(1),
15860
+ /** Values for the action's declared variables; the others take their default. */
15861
+ variables: record(string(), union([
15862
+ string(),
15863
+ number(),
15864
+ boolean()
15865
+ ])).optional()
15589
15866
  }),
15590
15867
  object({
15591
15868
  kind: literal("code"),
@@ -15594,10 +15871,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15594
15871
  code: string().min(1).max(2e4)
15595
15872
  })
15596
15873
  ]);
15874
+ /**
15875
+ * How a trigger that arrives while the automation is busy (or cooling down) is
15876
+ * handled. One of the automation's execution modes, never a property of its
15877
+ * actions. Absent means `single`.
15878
+ *
15879
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
15880
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
15881
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
15882
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
15883
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
15884
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
15885
+ * wait, never the single/queued limits.
15886
+ */
15887
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
15888
+ object({ mode: literal("single") }),
15889
+ object({ mode: literal("restart") }),
15890
+ object({
15891
+ mode: literal("queued"),
15892
+ max: number().int().min(1).max(10)
15893
+ }),
15894
+ object({
15895
+ mode: literal("parallel"),
15896
+ max: number().int().min(1).max(10)
15897
+ }),
15898
+ object({
15899
+ mode: literal("cooldown"),
15900
+ minutes: number().int().min(1).max(1440)
15901
+ })
15902
+ ]);
15597
15903
  var AutomationRecipeSchema = object({
15598
15904
  triggers: array(AutomationTriggerSchema),
15599
15905
  conditions: AutomationConditionSchema.optional(),
15600
- actions: array(AutomationActionSchema)
15906
+ actions: array(AutomationActionSchema),
15907
+ runMode: AutomationRunModeSchema.optional(),
15908
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
15909
+ keepTraces: number().int().min(1).max(100).optional()
15910
+ });
15911
+ new Set([
15912
+ {
15913
+ id: "person",
15914
+ name: "Person"
15915
+ },
15916
+ {
15917
+ id: "vehicle",
15918
+ name: "Vehicle"
15919
+ },
15920
+ {
15921
+ id: "animal",
15922
+ name: "Animal"
15923
+ },
15924
+ {
15925
+ id: "package",
15926
+ name: "Package"
15927
+ }
15928
+ ].map((l) => l.id));
15929
+ var COCO_TO_MACRO = {
15930
+ mapping: {
15931
+ person: "person",
15932
+ bicycle: "vehicle",
15933
+ car: "vehicle",
15934
+ motorcycle: "vehicle",
15935
+ airplane: "vehicle",
15936
+ bus: "vehicle",
15937
+ train: "vehicle",
15938
+ truck: "vehicle",
15939
+ boat: "vehicle",
15940
+ bird: "animal",
15941
+ cat: "animal",
15942
+ dog: "animal",
15943
+ horse: "animal",
15944
+ sheep: "animal",
15945
+ cow: "animal",
15946
+ elephant: "animal",
15947
+ bear: "animal",
15948
+ zebra: "animal",
15949
+ giraffe: "animal",
15950
+ suitcase: "package",
15951
+ backpack: "package",
15952
+ handbag: "package"
15953
+ },
15954
+ preserveOriginal: false
15955
+ };
15956
+ new Set(Object.values(COCO_TO_MACRO.mapping));
15957
+ /**
15958
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
15959
+ * so the editor can paint every block green or red with its reason.
15960
+ *
15961
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
15962
+ * every condition node (containers and leaves, including the ones a
15963
+ * short-circuit never reached), the run-mode verdict and every action step —
15964
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
15965
+ * for an Action run). Skipped
15966
+ * evaluations are traced too, and identical consecutive skips collapse into a
15967
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
15968
+ *
15969
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
15970
+ * vocabulary so an Action run records the same step shape.
15971
+ */
15972
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
15973
+ var TraceValueSchema = union([
15974
+ string(),
15975
+ number(),
15976
+ boolean(),
15977
+ _null(),
15978
+ array(union([string(), number()]))
15979
+ ]);
15980
+ var AutomationRunOutcomeSchema = _enum([
15981
+ "ran",
15982
+ "skipped-trigger",
15983
+ "skipped-conditions",
15984
+ "skipped-cooldown",
15985
+ "skipped-overlap",
15986
+ "failed",
15987
+ "aborted"
15988
+ ]);
15989
+ /** `not-evaluated` = a short-circuit never reached the node. */
15990
+ var TraceNodeOutcomeSchema = _enum([
15991
+ "pass",
15992
+ "fail",
15993
+ "not-evaluated"
15994
+ ]);
15995
+ var AutomationTriggerTraceSchema = object({
15996
+ kind: string().min(1),
15997
+ outcome: _enum(["pass", "fail"]),
15998
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
15999
+ reason: string(),
16000
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
16001
+ vars: record(string(), TraceValueSchema)
16002
+ });
16003
+ var AutomationConditionTraceNodeSchema = object({
16004
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
16005
+ path: string().min(1),
16006
+ kind: _enum([
16007
+ "all",
16008
+ "any",
16009
+ "not",
16010
+ "condition",
16011
+ "expression",
16012
+ "stationary-present"
16013
+ ]),
16014
+ outcome: TraceNodeOutcomeSchema,
16015
+ reason: string(),
16016
+ deviceId: number().int().optional(),
16017
+ cap: string().optional(),
16018
+ fieldPath: string().optional(),
16019
+ operator: string().optional(),
16020
+ expected: TraceValueSchema.optional(),
16021
+ /** What the mirror held; `null` when it held nothing (unknown). */
16022
+ observed: TraceValueSchema.optional()
16023
+ });
16024
+ var AutomationRunVerdictSchema = object({
16025
+ mode: AutomationRunModeSchema,
16026
+ decision: _enum([
16027
+ "run",
16028
+ "queued",
16029
+ "restart",
16030
+ "drop",
16031
+ "cooldown",
16032
+ "bypass"
16033
+ ]),
16034
+ reason: string()
16035
+ });
16036
+ /** One executed (or not-run) step — shared by automations and Action runs. */
16037
+ var AutomationStepTraceSchema = object({
16038
+ /** `actions.<i>` */
16039
+ path: string().min(1),
16040
+ kind: string().min(1),
16041
+ cap: string().optional(),
16042
+ method: string().optional(),
16043
+ deviceId: number().int().optional(),
16044
+ startedAt: number().optional(),
16045
+ endedAt: number().optional(),
16046
+ outcome: _enum([
16047
+ "ok",
16048
+ "error",
16049
+ "not-run"
16050
+ ]),
16051
+ message: string().optional()
16052
+ });
16053
+ var AutomationRunTraceSchema = object({
16054
+ runId: string().min(1),
16055
+ startedAt: number(),
16056
+ endedAt: number(),
16057
+ trigger: AutomationTriggerTraceSchema,
16058
+ conditions: array(AutomationConditionTraceNodeSchema),
16059
+ verdict: AutomationRunVerdictSchema.optional(),
16060
+ steps: array(AutomationStepTraceSchema),
16061
+ outcome: AutomationRunOutcomeSchema,
16062
+ /** How many identical consecutive skips this trace stands for (>= 1). */
16063
+ repeat: number().int().min(1),
16064
+ /** When the latest of those repeats happened. */
16065
+ lastAt: number()
16066
+ });
16067
+ /**
16068
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
16069
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
16070
+ */
16071
+ var AutomationStateDocSchema = object({
16072
+ version: literal(1),
16073
+ lastRunStartedAt: number().nullable(),
16074
+ traces: array(AutomationRunTraceSchema)
16075
+ });
16076
+ /**
16077
+ * Actions — named, runnable things an operator (or an automation) can start,
16078
+ * declared as LIST ENTRIES, never as devices.
16079
+ *
16080
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
16081
+ * hub's `actions` record kind) or declared by an addon through the
16082
+ * `action-source` collection (a Home Assistant script, a scene …). The one
16083
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
16084
+ * catalog of its own (D3) — every `list` asks the providers live.
16085
+ *
16086
+ * This file is the data half: the entry schema, the variables an entry may
16087
+ * declare, and the pure resolution of the values a run was given. It holds no
16088
+ * runtime.
16089
+ */
16090
+ /** What a variable's value is. `select` takes one of `options`. */
16091
+ var ActionVariableTypeSchema = _enum([
16092
+ "string",
16093
+ "number",
16094
+ "boolean",
16095
+ "select"
16096
+ ]);
16097
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
16098
+ var ActionVariableValueSchema = union([
16099
+ string(),
16100
+ number(),
16101
+ boolean()
16102
+ ]);
16103
+ /**
16104
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
16105
+ * value) or `{{vars.name}}` (inside a string) in their args.
16106
+ */
16107
+ var ActionVariableSchema = object({
16108
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
16109
+ label: string().min(1).max(120).optional(),
16110
+ type: ActionVariableTypeSchema,
16111
+ required: boolean().optional(),
16112
+ default: ActionVariableValueSchema.optional(),
16113
+ /** The allowed values of a `select`. */
16114
+ options: array(string().min(1)).min(1).optional()
16115
+ }).superRefine((v, ctx) => {
16116
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
16117
+ code: "custom",
16118
+ message: "a select variable needs options",
16119
+ path: ["options"]
16120
+ });
16121
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
16122
+ code: "custom",
16123
+ message: variableValueError(v, v.default) ?? "invalid default",
16124
+ path: ["default"]
16125
+ });
16126
+ });
16127
+ /** Why `value` is not a legal value for the variable, or null when it is. */
16128
+ function variableValueError(variable, value) {
16129
+ switch (variable.type) {
16130
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
16131
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
16132
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
16133
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
16134
+ }
16135
+ }
16136
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
16137
+ function isInternalActionId(actionId) {
16138
+ return actionId.startsWith("internal:") && actionId.length > 9;
16139
+ }
16140
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
16141
+ function addonIdOfActionId(actionId) {
16142
+ if (isInternalActionId(actionId)) return null;
16143
+ const at = actionId.indexOf(":");
16144
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
16145
+ }
16146
+ var ActionStatusSchema = object({
16147
+ isRunning: boolean(),
16148
+ /** Ms epoch the latest run STARTED; null when it never ran. */
16149
+ lastRunAt: number().nullable(),
16150
+ /** The latest run's failure; null on success or when it never ran. */
16151
+ lastError: string().nullable()
16152
+ });
16153
+ var ActionEntrySchema = object({
16154
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
16155
+ id: string().min(3),
16156
+ name: string().min(1).max(120),
16157
+ /** The addon that declared it; `core-blocks` for an internal one. */
16158
+ addonId: string().min(1),
16159
+ source: ActionSourceKindSchema,
16160
+ /** An addon-declared entry is edited where it was declared. */
16161
+ readOnly: boolean(),
16162
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
16163
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
16164
+ variables: array(ActionVariableSchema).max(20).optional(),
16165
+ status: ActionStatusSchema
16166
+ }).superRefine((entry, ctx) => {
16167
+ if (entry.source === "internal") {
16168
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
16169
+ code: "custom",
16170
+ message: "an internal action id is internal:<uuid>",
16171
+ path: ["id"]
16172
+ });
16173
+ return;
16174
+ }
16175
+ if (entry.steps !== void 0) ctx.addIssue({
16176
+ code: "custom",
16177
+ message: "only an internal action carries steps",
16178
+ path: ["steps"]
16179
+ });
16180
+ if (!entry.readOnly) ctx.addIssue({
16181
+ code: "custom",
16182
+ message: "an addon action is read-only here",
16183
+ path: ["readOnly"]
16184
+ });
16185
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
16186
+ code: "custom",
16187
+ message: "an addon action id is <addonId>:<localId>",
16188
+ path: ["id"]
16189
+ });
16190
+ });
16191
+ /** What an operator writes for an internal action. */
16192
+ var ActionInputSchema = object({
16193
+ name: string().min(1).max(120),
16194
+ steps: array(ActionStepSchema).min(1).max(50),
16195
+ variables: array(ActionVariableSchema).max(20).optional()
16196
+ });
16197
+ /** Who started a run. Absent = the operator pressed it. */
16198
+ var ActionRunOriginSchema = object({
16199
+ /** The automation block whose step started it. */
16200
+ blockId: string().min(1).optional() });
16201
+ /** `started` = the caller did not ask to wait. */
16202
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
16203
+ var ActionRunResultSchema = object({
16204
+ runId: string().min(1),
16205
+ outcome: ActionRunOutcomeSchema,
16206
+ /** Why it did not run, or what failed. */
16207
+ message: string().optional()
16208
+ });
16209
+ _enum([
16210
+ "not-found",
16211
+ "read-only",
16212
+ "invalid-variables",
16213
+ "invalid-steps",
16214
+ "not-linked",
16215
+ "busy"
16216
+ ]);
16217
+ object({
16218
+ actionId: string(),
16219
+ runId: string(),
16220
+ isRunning: boolean(),
16221
+ /** Set when the run ended. */
16222
+ outcome: AutomationRunOutcomeSchema.optional(),
16223
+ lastRunAt: number().nullable(),
16224
+ lastError: string().nullable()
15601
16225
  });
15602
16226
  /**
15603
16227
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -17192,7 +17816,22 @@ var BrokerProviderInfoSchema = object({
17192
17816
  */
17193
17817
  interactiveConnectPath: string().min(1).optional(),
17194
17818
  /** Relative hub path to disconnect the linked account (same popup pattern). */
17195
- interactiveDisconnectPath: string().min(1).optional()
17819
+ interactiveDisconnectPath: string().min(1).optional(),
17820
+ /**
17821
+ * The kind signs in through a server-held interactive session instead of
17822
+ * (or before) typed credentials. `basePath` is a hub-relative route set
17823
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
17824
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
17825
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
17826
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
17827
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
17828
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
17829
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
17830
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
17831
+ * offers it as the default path; the fields the session fills are
17832
+ * marked `filledBySignIn` in the kind's settings schema.
17833
+ */
17834
+ signIn: object({ basePath: string().min(1) }).optional()
17196
17835
  }))
17197
17836
  });
17198
17837
  var ListInputSchema = object({
@@ -18318,7 +18957,7 @@ var CompositionEventPlanSchema = object({
18318
18957
  stateField: string(),
18319
18958
  fieldKey: string()
18320
18959
  });
18321
- /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
18960
+ /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
18322
18961
  var CompositionProblemSchema = object({
18323
18962
  code: _enum([
18324
18963
  "reserved-variant",
@@ -18346,7 +18985,8 @@ var CompositionProblemSchema = object({
18346
18985
  "native-capability-replace",
18347
18986
  "mixed-quantity",
18348
18987
  "helper-mismatch",
18349
- "member-constraint"
18988
+ "member-constraint",
18989
+ "retention"
18350
18990
  ]),
18351
18991
  path: string(),
18352
18992
  message: string()
@@ -18580,13 +19220,17 @@ Object.freeze({
18580
19220
  latchedAt: {
18581
19221
  kind: "fixed",
18582
19222
  result: ["number", "null"]
19223
+ },
19224
+ schmitt: {
19225
+ kind: "fixed",
19226
+ result: ["boolean", "null"]
18583
19227
  }
18584
19228
  });
18585
19229
  /**
18586
19230
  * One catalog row per base reading a group helper can build (D944 §3).
18587
19231
  * Data, not a form default written into the block.
18588
19232
  */
18589
- var NOW = {
19233
+ var NOW$2 = {
18590
19234
  fieldPath: "lastFetchedAt",
18591
19235
  expr: "now"
18592
19236
  };
@@ -18600,6 +19244,8 @@ function numeric(input) {
18600
19244
  family: "numeric",
18601
19245
  deviceType: DeviceType.Sensor,
18602
19246
  memberDeviceType: DeviceType.Sensor,
19247
+ memberDeviceTypes: [DeviceType.Sensor],
19248
+ activeWhen: void 0,
18603
19249
  commandCaps: [],
18604
19250
  baseUnit: baseUnitOf(input.quantity),
18605
19251
  aggregates: NUMERIC_AGGREGATES,
@@ -18615,6 +19261,8 @@ function binary(input) {
18615
19261
  family: "binary",
18616
19262
  deviceType: DeviceType.Sensor,
18617
19263
  memberDeviceType: DeviceType.Sensor,
19264
+ memberDeviceTypes: [DeviceType.Sensor],
19265
+ activeWhen: void 0,
18618
19266
  commandCaps: [],
18619
19267
  quantity: "none",
18620
19268
  baseUnit: void 0,
@@ -18628,73 +19276,73 @@ var GROUP_TEMPLATES = [
18628
19276
  summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18629
19277
  role: DeviceRole.TemperatureSensor,
18630
19278
  outputCap: "temperature-sensor",
18631
- outputField: "celsius",
19279
+ outputField: "temperature",
18632
19280
  quantity: "temperature",
18633
19281
  defaultAggregate: "mean",
18634
19282
  empty: "null",
18635
- companions: [NOW]
19283
+ companions: [NOW$2]
18636
19284
  }),
18637
19285
  numeric({
18638
19286
  id: "humidity",
18639
19287
  summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18640
19288
  role: DeviceRole.HumiditySensor,
18641
19289
  outputCap: "humidity-sensor",
18642
- outputField: "percent",
19290
+ outputField: "humidity",
18643
19291
  quantity: "humidity",
18644
19292
  defaultAggregate: "mean",
18645
19293
  empty: "null",
18646
- companions: [NOW]
19294
+ companions: [NOW$2]
18647
19295
  }),
18648
19296
  numeric({
18649
19297
  id: "pressure",
18650
19298
  summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18651
19299
  role: DeviceRole.PressureSensor,
18652
19300
  outputCap: "pressure-sensor",
18653
- outputField: "hpa",
19301
+ outputField: "pressure",
18654
19302
  quantity: "pressure",
18655
19303
  defaultAggregate: "mean",
18656
19304
  empty: "null",
18657
- companions: [NOW]
19305
+ companions: [NOW$2]
18658
19306
  }),
18659
19307
  numeric({
18660
19308
  id: "illuminance",
18661
19309
  summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18662
19310
  role: DeviceRole.AmbientLightSensor,
18663
19311
  outputCap: "ambient-light-sensor",
18664
- outputField: "lux",
19312
+ outputField: "illuminance",
18665
19313
  quantity: "illuminance",
18666
19314
  defaultAggregate: "mean",
18667
19315
  empty: "null",
18668
- companions: [NOW]
19316
+ companions: [NOW$2]
18669
19317
  }),
18670
19318
  numeric({
18671
19319
  id: "power",
18672
19320
  summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18673
19321
  role: DeviceRole.PowerSensor,
18674
19322
  outputCap: "power-meter",
18675
- outputField: "watts",
19323
+ outputField: "power",
18676
19324
  quantity: "power",
18677
19325
  defaultAggregate: "sum",
18678
19326
  empty: "omit",
18679
- companions: [NOW]
19327
+ companions: [NOW$2]
18680
19328
  }),
18681
19329
  numeric({
18682
19330
  id: "energy",
18683
19331
  summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18684
19332
  role: DeviceRole.EnergySensor,
18685
19333
  outputCap: "power-meter",
18686
- outputField: "kwhTotal",
19334
+ outputField: "energy",
18687
19335
  quantity: "energy",
18688
19336
  defaultAggregate: "sum",
18689
19337
  empty: "omit",
18690
- companions: [NOW]
19338
+ companions: [NOW$2]
18691
19339
  }),
18692
19340
  numeric({
18693
19341
  id: "battery",
18694
19342
  summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18695
19343
  role: DeviceRole.BatterySensor,
18696
19344
  outputCap: "battery",
18697
- outputField: "percentage",
19345
+ outputField: "level",
18698
19346
  quantity: "percentage",
18699
19347
  defaultAggregate: "mean",
18700
19348
  empty: "null",
@@ -18722,7 +19370,7 @@ var GROUP_TEMPLATES = [
18722
19370
  quantity: "device",
18723
19371
  defaultAggregate: "mean",
18724
19372
  empty: "null",
18725
- companions: [NOW]
19373
+ companions: [NOW$2]
18726
19374
  }),
18727
19375
  binary({
18728
19376
  id: "motion",
@@ -18816,6 +19464,11 @@ var GROUP_TEMPLATES = [
18816
19464
  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.",
18817
19465
  deviceType: DeviceType.Switch,
18818
19466
  outputCap: "switch",
19467
+ outputField: "on",
19468
+ member: {
19469
+ cap: "switch",
19470
+ fieldPath: "on"
19471
+ },
18819
19472
  commandCaps: ["switch"]
18820
19473
  }),
18821
19474
  actuator({
@@ -18823,34 +19476,136 @@ var GROUP_TEMPLATES = [
18823
19476
  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.",
18824
19477
  deviceType: DeviceType.Light,
18825
19478
  outputCap: "switch",
19479
+ outputField: "on",
19480
+ member: {
19481
+ cap: "switch",
19482
+ fieldPath: "on"
19483
+ },
18826
19484
  commandCaps: [
18827
19485
  "switch",
18828
19486
  "brightness",
18829
19487
  "color"
18830
19488
  ]
19489
+ }),
19490
+ actuator({
19491
+ id: "fan",
19492
+ 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.",
19493
+ deviceType: DeviceType.Fan,
19494
+ outputCap: "switch",
19495
+ outputField: "on",
19496
+ member: {
19497
+ cap: "switch",
19498
+ fieldPath: "on"
19499
+ },
19500
+ commandCaps: ["switch", "fan-control"]
19501
+ }),
19502
+ actuator({
19503
+ id: "cover",
19504
+ 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.",
19505
+ deviceType: DeviceType.Cover,
19506
+ outputCap: "group-active",
19507
+ outputField: "active",
19508
+ member: {
19509
+ cap: "cover",
19510
+ fieldPath: "state"
19511
+ },
19512
+ activeWhen: [
19513
+ "open",
19514
+ "opening",
19515
+ "stopped"
19516
+ ],
19517
+ commandCaps: ["cover"]
19518
+ }),
19519
+ actuator({
19520
+ id: "valve",
19521
+ 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.",
19522
+ deviceType: DeviceType.Valve,
19523
+ outputCap: "group-active",
19524
+ outputField: "active",
19525
+ member: {
19526
+ cap: "valve",
19527
+ fieldPath: "state"
19528
+ },
19529
+ activeWhen: [
19530
+ "open",
19531
+ "opening",
19532
+ "stopped"
19533
+ ],
19534
+ commandCaps: ["valve"]
19535
+ }),
19536
+ actuator({
19537
+ id: "lock",
19538
+ 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.",
19539
+ deviceType: DeviceType.Lock,
19540
+ outputCap: "group-active",
19541
+ outputField: "active",
19542
+ member: {
19543
+ cap: "lock-control",
19544
+ fieldPath: "state"
19545
+ },
19546
+ activeWhen: ["locked"],
19547
+ commandCaps: ["lock-control"]
19548
+ }),
19549
+ actuator({
19550
+ id: "media-player",
19551
+ 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.",
19552
+ deviceType: DeviceType.MediaPlayer,
19553
+ outputCap: "group-active",
19554
+ outputField: "active",
19555
+ member: {
19556
+ cap: "media-player",
19557
+ fieldPath: "state"
19558
+ },
19559
+ activeWhen: ["playing", "buffering"],
19560
+ commandCaps: ["media-player"]
19561
+ }),
19562
+ actuator({
19563
+ id: "climate",
19564
+ 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.",
19565
+ deviceType: DeviceType.Thermostat,
19566
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
19567
+ outputCap: "group-active",
19568
+ outputField: "active",
19569
+ member: {
19570
+ cap: "climate-control",
19571
+ fieldPath: "mode"
19572
+ },
19573
+ activeWhen: [
19574
+ "heat",
19575
+ "cool",
19576
+ "auto",
19577
+ "heat_cool",
19578
+ "fan_only",
19579
+ "dry"
19580
+ ],
19581
+ commandCaps: ["climate-control"]
18831
19582
  })
18832
19583
  ];
18833
19584
  function actuator(input) {
18834
19585
  return {
18835
- ...input,
19586
+ id: input.id,
19587
+ summary: input.summary,
19588
+ deviceType: input.deviceType,
19589
+ outputCap: input.outputCap,
19590
+ outputField: input.outputField,
19591
+ commandCaps: input.commandCaps,
18836
19592
  family: "actuator",
18837
19593
  role: void 0,
18838
19594
  memberDeviceType: input.deviceType,
18839
- outputField: "on",
19595
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
19596
+ activeWhen: input.activeWhen,
18840
19597
  quantity: "none",
18841
19598
  baseUnit: void 0,
18842
19599
  aggregates: BINARY_AGGREGATES,
18843
19600
  defaultAggregate: "any",
18844
19601
  empty: "offline",
18845
- accept: [{
18846
- cap: "switch",
18847
- fieldPath: "on"
18848
- }],
19602
+ accept: [input.member],
18849
19603
  companions: [CHANGED]
18850
19604
  };
18851
19605
  }
18852
19606
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
18853
19607
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
19608
+ DeviceType.Camera;
18854
19609
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
18855
19610
  /**
18856
19611
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -19165,7 +19920,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
19165
19920
  kind: "mutation",
19166
19921
  auth: "admin",
19167
19922
  access: "view"
19168
- });
19923
+ }), method(object({
19924
+ blockId: string().min(1),
19925
+ trace: AutomationRunTraceSchema.optional(),
19926
+ runStartedAt: number().optional(),
19927
+ keepTraces: number().int().min(1).max(100).optional()
19928
+ }), _void(), {
19929
+ kind: "mutation",
19930
+ auth: "admin"
19931
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
19932
+ traces: array(AutomationRunTraceSchema),
19933
+ lastError: string().nullable()
19934
+ }), { auth: "admin" });
19169
19935
  /**
19170
19936
  * `custom-model-registry` — collection cap exposing operator-registered
19171
19937
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -44261,7 +45027,7 @@ var NetworkAccessStatusSchema = object({
44261
45027
  });
44262
45028
  /**
44263
45029
  * Optional, richer endpoint shape returned by providers that expose
44264
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
45030
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44265
45031
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44266
45032
  * the originating provider config (mode + sourcePort) so the
44267
45033
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -44654,51 +45420,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
44654
45420
  kind: "mutation",
44655
45421
  auth: "admin"
44656
45422
  });
44657
- new Set([
44658
- {
44659
- id: "person",
44660
- name: "Person"
44661
- },
44662
- {
44663
- id: "vehicle",
44664
- name: "Vehicle"
44665
- },
44666
- {
44667
- id: "animal",
44668
- name: "Animal"
44669
- },
44670
- {
44671
- id: "package",
44672
- name: "Package"
44673
- }
44674
- ].map((l) => l.id));
44675
- var COCO_TO_MACRO = {
44676
- mapping: {
44677
- person: "person",
44678
- bicycle: "vehicle",
44679
- car: "vehicle",
44680
- motorcycle: "vehicle",
44681
- airplane: "vehicle",
44682
- bus: "vehicle",
44683
- train: "vehicle",
44684
- truck: "vehicle",
44685
- boat: "vehicle",
44686
- bird: "animal",
44687
- cat: "animal",
44688
- dog: "animal",
44689
- horse: "animal",
44690
- sheep: "animal",
44691
- cow: "animal",
44692
- elephant: "animal",
44693
- bear: "animal",
44694
- zebra: "animal",
44695
- giraffe: "animal",
44696
- suitcase: "package",
44697
- backpack: "package",
44698
- handbag: "package"
44699
- },
44700
- preserveOriginal: false
44701
- };
44702
45423
  var AUDIO_MACRO_LABELS = [
44703
45424
  {
44704
45425
  id: "speech",
@@ -46948,7 +47669,20 @@ var NcRuleInputSchema = object({
46948
47669
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
46949
47670
  * path does not run Zod defaults.
46950
47671
  */
46951
- groupingSec: number().int().min(5).max(60).optional()
47672
+ groupingSec: number().int().min(5).max(60).optional(),
47673
+ /**
47674
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47675
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47676
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47677
+ * same mark the Library writes, and records whose notification it was and
47678
+ * when. A channel with no buttons gets the same two choices as links.
47679
+ *
47680
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47681
+ * cap path): absent and `false` both mean off, which is what every rule
47682
+ * authored before this field meant. An editor turns it off by writing
47683
+ * `false`, so it needs no `clear` key.
47684
+ */
47685
+ askVerdict: boolean().optional()
46952
47686
  });
46953
47687
  /**
46954
47688
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -57252,9 +57986,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
57252
57986
  */
57253
57987
  var AirQualitySensorStatusSchema = object({
57254
57988
  /** Carbon dioxide concentration in ppm. */
57255
- co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
57989
+ co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
57256
57990
  /** Total volatile organic compounds in ppb. */
57257
- vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57991
+ voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57258
57992
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
57259
57993
  pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
57260
57994
  /** Particulate matter ≤ 10 μm in µg/m³. */
@@ -57287,12 +58021,12 @@ var airQualitySensorCapability = {
57287
58021
  * History (D850): recorded series.
57288
58022
  */
57289
58023
  history: { fields: {
57290
- co2Ppm: {
58024
+ co2: {
57291
58025
  kind: "gauge",
57292
58026
  unit: "ppm",
57293
58027
  deadband: 5
57294
58028
  },
57295
- vocPpb: {
58029
+ voc: {
57296
58030
  kind: "gauge",
57297
58031
  unit: "ppb",
57298
58032
  deadband: 5
@@ -57323,7 +58057,7 @@ var airQualitySensorCapability = {
57323
58057
  */
57324
58058
  var AmbientLightSensorStatusSchema = object({
57325
58059
  /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
57326
- lux: number().min(0).nullable().meta(readingMeta("illuminance", {
58060
+ illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
57327
58061
  precision: 0,
57328
58062
  label: "Illuminance"
57329
58063
  })),
@@ -57352,7 +58086,7 @@ var ambientLightSensorCapability = {
57352
58086
  /**
57353
58087
  * History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
57354
58088
  */
57355
- history: { fields: { lux: {
58089
+ history: { fields: { illuminance: {
57356
58090
  kind: "gauge",
57357
58091
  unit: "lx",
57358
58092
  deadbandPct: 10,
@@ -57491,6 +58225,84 @@ var audioMetricsCapability = {
57491
58225
  */
57492
58226
  history: "none"
57493
58227
  };
58228
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58229
+ actionId: string().min(1),
58230
+ variables: record(string(), ActionVariableValueSchema).optional()
58231
+ }), _void(), {
58232
+ kind: "mutation",
58233
+ auth: "admin"
58234
+ }), method(object({ actionId: string().min(1) }), _void(), {
58235
+ kind: "mutation",
58236
+ auth: "admin"
58237
+ });
58238
+ /**
58239
+ * actions — the ONE surface for runnable actions, for the UI and for
58240
+ * automations. Provided by the core-blocks builtin on the hub.
58241
+ *
58242
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
58243
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
58244
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
58245
+ * the run status from the hub's `runs` ledger; there is no catalog here to
58246
+ * drift (D3).
58247
+ *
58248
+ * **Permissions follow the camera, as linked devices do.** An action is linked
58249
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
58250
+ * non-admin may `get` / `run` an action only through a camera they may see that
58251
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
58252
+ * gate sees it) and the provider refuses an action that camera does not link.
58253
+ * An admin, and an in-process caller (an automation block), may run any action.
58254
+ * Every refusal is typed (`[not-linked] …`) and logged.
58255
+ *
58256
+ * A second `run` of an action that is already running is REFUSED — traced and
58257
+ * logged — never queued.
58258
+ */
58259
+ var ActionIdInput = object({ actionId: string().min(1) });
58260
+ var ActionLinksSchema = object({
58261
+ deviceId: number().int().nonnegative(),
58262
+ actionIds: array(string().min(1)).max(100)
58263
+ });
58264
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58265
+ actionId: string().min(1),
58266
+ deviceId: number().int().nonnegative().optional()
58267
+ }), object({ action: ActionEntrySchema.nullable() }), {
58268
+ role: "viewer",
58269
+ auth: "protected",
58270
+ kind: "query",
58271
+ caller: "required"
58272
+ }), method(object({
58273
+ actionId: string().min(1),
58274
+ variables: record(string(), ActionVariableValueSchema).optional(),
58275
+ wait: boolean().optional(),
58276
+ origin: ActionRunOriginSchema.optional(),
58277
+ deviceId: number().int().nonnegative().optional()
58278
+ }), ActionRunResultSchema, {
58279
+ role: "viewer",
58280
+ auth: "protected",
58281
+ kind: "mutation",
58282
+ caller: "required"
58283
+ }), method(ActionIdInput, _void(), {
58284
+ kind: "mutation",
58285
+ auth: "admin"
58286
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
58287
+ kind: "mutation",
58288
+ auth: "admin"
58289
+ }), method(object({
58290
+ actionId: string().min(1),
58291
+ action: ActionInputSchema.partial()
58292
+ }), object({ action: ActionEntrySchema }), {
58293
+ kind: "mutation",
58294
+ auth: "admin"
58295
+ }), method(ActionIdInput, _void(), {
58296
+ kind: "mutation",
58297
+ auth: "admin"
58298
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
58299
+ role: "viewer",
58300
+ auth: "protected",
58301
+ kind: "query"
58302
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
58303
+ kind: "mutation",
58304
+ auth: "admin"
58305
+ });
57494
58306
  /**
57495
58307
  * Automation-control cap. Models HA `automation.*` entities on
57496
58308
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -57587,7 +58399,7 @@ var BatteryStatusSchema = object({
57587
58399
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
57588
58400
  * declines to band a non-finite reading, which is the correct shape.
57589
58401
  */
57590
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
58402
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
57591
58403
  /**
57592
58404
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
57593
58405
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -57597,7 +58409,7 @@ var BatteryStatusSchema = object({
57597
58409
  * `'unknown'` is the absence of an answer: the provider has not read the
57598
58410
  * power fields yet, or the firmware reported neither. Before it existed,
57599
58411
  * two unreadable fields produced `'none'`, and a camera nobody had reached
57600
- * was drawn "on battery alone" — the same D315 defect `percentage` closed
58412
+ * was drawn "on battery alone" — the same D315 defect `level` closed
57601
58413
  * by going nullable. Consumers SKIP it (no charger state published, no
57602
58414
  * glyph, no HomeKit ChargingState) rather than coerce it either way.
57603
58415
  */
@@ -57621,7 +58433,7 @@ var BatteryStatusSchema = object({
57621
58433
  * that only report charge. A second reading on this cap, not a second
57622
58434
  * `numeric-sensor` on the same device.
57623
58435
  */
57624
- volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
58436
+ voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
57625
58437
  /**
57626
58438
  * Ms epoch of the last time the device PROVED it was reachable — a
57627
58439
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -57655,7 +58467,7 @@ var BatteryStatusSchema = object({
57655
58467
  /**
57656
58468
  * True when the source is a BINARY low-battery indicator (HA
57657
58469
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
57658
- * charge level — `percentage` is then a coarse stand-in (100 = normal,
58470
+ * charge level — `level` is then a coarse stand-in (100 = normal,
57659
58471
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
57660
58472
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
57661
58473
  */
@@ -57668,21 +58480,22 @@ var BATTERY_DEVICE_TYPES = [
57668
58480
  DeviceType.Switch,
57669
58481
  DeviceType.PetFeeder,
57670
58482
  DeviceType.Container,
57671
- DeviceType.Thermostat
58483
+ DeviceType.Thermostat,
58484
+ DeviceType.EventEmitter
57672
58485
  ];
57673
58486
  /** Generic Customize target for the percentage reading (D936). */
57674
58487
  var batteryCustomize = {
57675
58488
  version: 1,
57676
58489
  targets: [{
57677
- id: "percentage",
58490
+ id: "level",
57678
58491
  cardinality: "scalar",
57679
58492
  vocabulary: {
57680
58493
  cap: "battery",
57681
- stateField: "percentage"
58494
+ stateField: "level"
57682
58495
  },
57683
58496
  deviceTypes: BATTERY_DEVICE_TYPES,
57684
58497
  apply: "add",
57685
- path: "percentage",
58498
+ path: "level",
57686
58499
  sources: [{
57687
58500
  kind: "field",
57688
58501
  valueKinds: ["number", "string"],
@@ -57776,7 +58589,7 @@ onStatusChanged: { data: object({
57776
58589
  schema: BatteryStatusSchema,
57777
58590
  kind: "push",
57778
58591
  empty: {
57779
- percentage: null,
58592
+ level: null,
57780
58593
  charging: "unknown",
57781
58594
  sleeping: false,
57782
58595
  lastUpdated: 0
@@ -57803,11 +58616,11 @@ onStatusChanged: { data: object({
57803
58616
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
57804
58617
  */
57805
58618
  history: { fields: {
57806
- percentage: {
58619
+ level: {
57807
58620
  kind: "gauge",
57808
58621
  unit: "%"
57809
58622
  },
57810
- volts: {
58623
+ voltage: {
57811
58624
  kind: "gauge",
57812
58625
  unit: "V"
57813
58626
  }
@@ -57867,14 +58680,14 @@ var binaryCapability = {
57867
58680
  * controls to this single-method surface.
57868
58681
  *
57869
58682
  * The cap is intentionally minimal: a single `setBrightness({deviceId,
57870
- * percentage})` mutation plus the auto-injected `getStatus`. Drivers
58683
+ * level})` mutation plus the auto-injected `getStatus`. Drivers
57871
58684
  * that expose richer controls (color temperature, scenes, schedules)
57872
58685
  * should surface those via the device's `getSettingsUISchema()`
57873
58686
  * instead of bloating this cap.
57874
58687
  */
57875
58688
  var BrightnessStatusSchema = object({
57876
58689
  /** Current level as 0..100 inclusive. Firmware-reported. */
57877
- percentage: number().min(0).max(100).meta(readingMeta("percentage")),
58690
+ level: number().min(0).max(100).meta(readingMeta("percentage")),
57878
58691
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
57879
58692
  lastChangedAt: number()
57880
58693
  });
@@ -57886,7 +58699,7 @@ var brightnessCapability = {
57886
58699
  deviceTypes: [DeviceType.Light],
57887
58700
  methods: { setBrightness: method(object({
57888
58701
  deviceId: number().int().nonnegative(),
57889
- percentage: number().min(0).max(100)
58702
+ level: number().min(0).max(100)
57890
58703
  }), _void(), {
57891
58704
  kind: "mutation",
57892
58705
  auth: "protected"
@@ -57899,7 +58712,7 @@ var brightnessCapability = {
57899
58712
  */
57900
58713
  onBrightnessChanged: { data: object({
57901
58714
  deviceId: number(),
57902
- percentage: number().min(0).max(100),
58715
+ level: number().min(0).max(100),
57903
58716
  lastChangedAt: number()
57904
58717
  }) } },
57905
58718
  status: {
@@ -57922,7 +58735,7 @@ onBrightnessChanged: { data: object({
57922
58735
  /**
57923
58736
  * History (D850): recorded series. Actuator tier: level keeps every change.
57924
58737
  */
57925
- history: { fields: { percentage: {
58738
+ history: { fields: { level: {
57926
58739
  kind: "gauge",
57927
58740
  unit: "%",
57928
58741
  minIntervalMs: 0
@@ -58519,11 +59332,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
58519
59332
  }),
58520
59333
  object({
58521
59334
  mode: literal("mired"),
58522
- mireds: number().int().min(50).max(1e3)
59335
+ colorTemperature: number().int().min(50).max(1e3)
58523
59336
  })
58524
59337
  ]);
58525
59338
  var ColorStatusSchema = object({
58526
- /** Active color mode — which of `rgb` / `hsv` / `mireds` reflects
59339
+ /** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
58527
59340
  * the bulb's current state. */
58528
59341
  mode: _enum([
58529
59342
  "rgb",
@@ -58535,7 +59348,7 @@ var ColorStatusSchema = object({
58535
59348
  /** Populated when `mode === 'hsv'`. */
58536
59349
  hsv: HsvTripletSchema.optional(),
58537
59350
  /** Populated when `mode === 'mired'`. */
58538
- mireds: number().int().optional().meta(readingMeta("colour-temperature")),
59351
+ colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
58539
59352
  /** Ms epoch of the last operator-driven change. */
58540
59353
  lastChangedAt: number()
58541
59354
  });
@@ -58567,7 +59380,7 @@ onColorChanged: { data: object({
58567
59380
  ]),
58568
59381
  rgb: RgbTripletSchema.optional(),
58569
59382
  hsv: HsvTripletSchema.optional(),
58570
- mireds: number().int().optional(),
59383
+ colorTemperature: number().int().optional(),
58571
59384
  lastChangedAt: number()
58572
59385
  }) } },
58573
59386
  status: {
@@ -59444,11 +60257,11 @@ var DishwasherStatusSchema = object({
59444
60257
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
59445
60258
  availablePrograms: array(string()),
59446
60259
  /** Seconds remaining on the active cycle, or null when not running / not known. */
59447
- remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60260
+ remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59448
60261
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
59449
- delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60262
+ delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59450
60263
  /** Cycle completion 0..100, or null when not applicable. */
59451
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60264
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59452
60265
  /** True when the door is reported open; null when the door state is not known. */
59453
60266
  doorOpen: boolean().nullable(),
59454
60267
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -59667,14 +60480,14 @@ var dishwasherCapability = {
59667
60480
  runtimeState: DishwasherStatusSchema,
59668
60481
  durability: "session",
59669
60482
  history: { fields: {
59670
- progressPercent: {
60483
+ progress: {
59671
60484
  kind: "gauge",
59672
60485
  unit: "%",
59673
60486
  deadband: 2
59674
60487
  },
59675
60488
  state: { kind: "enum" },
59676
60489
  operationState: { kind: "enum" },
59677
- remainingSeconds: {
60490
+ remaining: {
59678
60491
  kind: "gauge",
59679
60492
  unit: "s",
59680
60493
  minIntervalMs: 6e4
@@ -60265,7 +61078,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
60265
61078
  var FanControlStatusSchema = object({
60266
61079
  /** Active speed as 0..100 inclusive. Null when the device has no
60267
61080
  * speed surface (single-speed fan). */
60268
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61081
+ speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60269
61082
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
60270
61083
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
60271
61084
  percentageStep: number().positive().optional().meta(readingMeta("percentage")),
@@ -60292,7 +61105,7 @@ var fanControlCapability = {
60292
61105
  methods: {
60293
61106
  setPercentage: method(object({
60294
61107
  deviceId: number().int().nonnegative(),
60295
- percentage: number().min(0).max(100)
61108
+ speed: number().min(0).max(100)
60296
61109
  }), _void(), {
60297
61110
  kind: "mutation",
60298
61111
  auth: "protected"
@@ -60325,7 +61138,7 @@ var fanControlCapability = {
60325
61138
  },
60326
61139
  /**
60327
61140
  * Runtime-state slice — mirrored by the kernel. UI fan speed
60328
- * sliders read `percentage` for live updates.
61141
+ * sliders read `speed` for live updates.
60329
61142
  */
60330
61143
  runtimeState: FanControlStatusSchema,
60331
61144
  /**
@@ -60339,7 +61152,7 @@ var fanControlCapability = {
60339
61152
  * History (D850): recorded series. Actuator tier: speed keeps every change.
60340
61153
  */
60341
61154
  history: { fields: {
60342
- percentage: {
61155
+ speed: {
60343
61156
  kind: "gauge",
60344
61157
  unit: "%",
60345
61158
  minIntervalMs: 0
@@ -60528,7 +61341,17 @@ var groupCommandsCapability = {
60528
61341
  scope: "device",
60529
61342
  deviceNative: true,
60530
61343
  mode: "singleton",
60531
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
61344
+ deviceTypes: [
61345
+ DeviceType.Switch,
61346
+ DeviceType.Light,
61347
+ DeviceType.Fan,
61348
+ DeviceType.Cover,
61349
+ DeviceType.Valve,
61350
+ DeviceType.Lock,
61351
+ DeviceType.MediaPlayer,
61352
+ DeviceType.Thermostat,
61353
+ DeviceType.Climate
61354
+ ],
60532
61355
  methods: {},
60533
61356
  status: {
60534
61357
  schema: GroupCommandsSchema,
@@ -60546,6 +61369,49 @@ var groupCommandsCapability = {
60546
61369
  history: "none"
60547
61370
  };
60548
61371
  /**
61372
+ * Whether a helper actuator group counts as active (D944): at least one
61373
+ * included member is, or every included member is. What "active" means is the
61374
+ * type's: a cover is open, a lock is locked, a media player is playing, a
61375
+ * climate device is not off. A group of lights, switches and fans reports
61376
+ * `switch.on` instead; this cap is for the types whose own state is not a
61377
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
61378
+ * empty included set takes the device offline, never `active: false`.
61379
+ */
61380
+ var GroupActiveSchema = object({
61381
+ active: boolean().meta({ label: "Active" }),
61382
+ /** Ms epoch when the slice was last updated. */
61383
+ lastChangedAt: number()
61384
+ });
61385
+ var groupActiveCapability = {
61386
+ name: "group-active",
61387
+ scope: "device",
61388
+ deviceNative: true,
61389
+ mode: "singleton",
61390
+ deviceTypes: [
61391
+ DeviceType.Cover,
61392
+ DeviceType.Valve,
61393
+ DeviceType.Lock,
61394
+ DeviceType.MediaPlayer,
61395
+ DeviceType.Climate,
61396
+ DeviceType.Thermostat
61397
+ ],
61398
+ methods: {},
61399
+ status: {
61400
+ schema: GroupActiveSchema,
61401
+ kind: "push"
61402
+ },
61403
+ runtimeState: GroupActiveSchema,
61404
+ /**
61405
+ * Runtime-state durability: **session** — recomputed from the members on the
61406
+ * next source event; a restored value would outlive the members it named.
61407
+ *
61408
+ * See `RuntimeStateDurability`. Enforced by
61409
+ * `scripts/check-runtime-state-durability.ts`.
61410
+ */
61411
+ durability: "session",
61412
+ history: "none"
61413
+ };
61414
+ /**
60549
61415
  * How many members of a helper group answered (D944). Counts are integers,
60550
61416
  * not measurements: zero included is a real count, never a stand-in reading.
60551
61417
  * The reading itself stays on the output capability. History is none — the
@@ -60576,7 +61442,14 @@ var groupStatusCapability = {
60576
61442
  deviceTypes: [
60577
61443
  DeviceType.Sensor,
60578
61444
  DeviceType.Switch,
60579
- DeviceType.Light
61445
+ DeviceType.Light,
61446
+ DeviceType.Fan,
61447
+ DeviceType.Cover,
61448
+ DeviceType.Valve,
61449
+ DeviceType.Lock,
61450
+ DeviceType.MediaPlayer,
61451
+ DeviceType.Thermostat,
61452
+ DeviceType.Climate
60580
61453
  ],
60581
61454
  methods: {},
60582
61455
  status: {
@@ -60724,7 +61597,7 @@ var humidifierCapability = {
60724
61597
  */
60725
61598
  var HumiditySensorStatusSchema = object({
60726
61599
  /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
60727
- percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
61600
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
60728
61601
  precision: 0,
60729
61602
  label: "Humidity"
60730
61603
  })),
@@ -60753,7 +61626,7 @@ var humiditySensorCapability = {
60753
61626
  /**
60754
61627
  * History (D850): recorded series.
60755
61628
  */
60756
- history: { fields: { percent: {
61629
+ history: { fields: { humidity: {
60757
61630
  kind: "gauge",
60758
61631
  unit: "%",
60759
61632
  deadband: .5
@@ -61393,7 +62266,7 @@ var LawnMowerControlStatusSchema = object({
61393
62266
  batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61394
62267
  /** 0..100 mowing-completion percentage of the current task, or null when no
61395
62268
  * task is active / progress is unavailable. */
61396
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62269
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61397
62270
  /** Current device/event code (dynamic — mostly status, sometimes an error),
61398
62271
  * or null when unknown. */
61399
62272
  currentCode: number().nullable().meta(readingMeta("none")),
@@ -61849,7 +62722,14 @@ var lockControlCapability = {
61849
62722
  }),
61850
62723
  unlock: method(object({
61851
62724
  deviceId: number().int().nonnegative(),
61852
- code: string().min(1).optional()
62725
+ code: string().min(1).optional(),
62726
+ /**
62727
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62728
+ * with one call is refused unless the caller says it was confirmed. A
62729
+ * single lock ignores it. The refusal lives in the callee, so no
62730
+ * automation or API caller reaches the doors by skipping a dialog.
62731
+ */
62732
+ confirm: boolean().optional()
61853
62733
  }), _void(), {
61854
62734
  kind: "mutation",
61855
62735
  auth: "protected"
@@ -61886,6 +62766,44 @@ var lockControlCapability = {
61886
62766
  volatileStateFields: ["lastChangedAt"]
61887
62767
  };
61888
62768
  /**
62769
+ * A monotonic total between resets (D944). Energy uses `power-meter.energy`
62770
+ * so dashboards keep one series; every other instantaneous reading lands here.
62771
+ * `value` is the time integral in the source quantity's accumulated base
62772
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
62773
+ */
62774
+ var MeterStatusSchema = object({
62775
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
62776
+ lastFetchedAt: number()
62777
+ });
62778
+ var meterCapability = {
62779
+ name: "meter",
62780
+ scope: "device",
62781
+ deviceNative: true,
62782
+ mode: "singleton",
62783
+ deviceTypes: [DeviceType.Sensor],
62784
+ methods: {},
62785
+ status: {
62786
+ schema: MeterStatusSchema,
62787
+ kind: "push"
62788
+ },
62789
+ runtimeState: MeterStatusSchema,
62790
+ /**
62791
+ * Runtime-state durability: **restored** — the total is a cache of the
62792
+ * history integral. A restart shows it only until the history pass
62793
+ * recomputes the open period; it is not an in-memory buffer.
62794
+ *
62795
+ * See `RuntimeStateDurability`. Enforced by
62796
+ * `scripts/check-runtime-state-durability.ts`.
62797
+ */
62798
+ durability: "restored",
62799
+ history: { fields: { value: {
62800
+ kind: "counter",
62801
+ deadband: .001,
62802
+ minIntervalMs: 6e4
62803
+ } } },
62804
+ volatileStateFields: ["lastFetchedAt"]
62805
+ };
62806
+ /**
61889
62807
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
61890
62808
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
61891
62809
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -61942,10 +62860,10 @@ var MediaPlayerStatusSchema = object({
61942
62860
  currentMedia: MediaInfoSchema.nullable(),
61943
62861
  /** Current playback position in ms. Null when not seekable or
61944
62862
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
61945
- positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62863
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
61946
62864
  /** Total duration of the current media in ms. Null when unknown
61947
62865
  * (live stream) or nothing is playing. */
61948
- durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62866
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
61949
62867
  /** Shuffle toggle. Null when no shuffle surface.
61950
62868
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
61951
62869
  shuffle: boolean().nullable(),
@@ -61984,7 +62902,7 @@ var mediaPlayerCapability = {
61984
62902
  }),
61985
62903
  seek: method(object({
61986
62904
  deviceId: number().int().nonnegative(),
61987
- positionMs: number().int().nonnegative()
62905
+ position: number().int().nonnegative()
61988
62906
  }), _void(), {
61989
62907
  kind: "mutation",
61990
62908
  auth: "protected"
@@ -62056,7 +62974,7 @@ var mediaPlayerCapability = {
62056
62974
  /**
62057
62975
  * History (D850): volume (a level an operator or an automation sets; the
62058
62976
  * default 10 s floor folds a slider drag into its end value) and mute.
62059
- * `positionMs` is not recorded: it moves continuously while playing.
62977
+ * `position` is not recorded: it moves continuously while playing.
62060
62978
  */
62061
62979
  history: { fields: {
62062
62980
  volumeLevel: {
@@ -63018,9 +63936,9 @@ var NetworkLinkStatusSchema = object({
63018
63936
  * one whose reading has not landed must not be drawn at 0 %. Consumers
63019
63937
  * SKIP a null rather than coerce it.
63020
63938
  */
63021
- signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63939
+ signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63022
63940
  /** Raw received signal strength in dBm, when the firmware reports one. */
63023
- rssiDbm: number().optional().meta(readingMeta("signal-level")),
63941
+ rssi: number().optional().meta(readingMeta("signal-level")),
63024
63942
  /** Network name of a wireless link, when the firmware reports it. */
63025
63943
  ssid: string().optional(),
63026
63944
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
@@ -63056,7 +63974,7 @@ onStatusChanged: { data: object({
63056
63974
  kind: "push",
63057
63975
  empty: {
63058
63976
  type: "unknown",
63059
- signalPercent: null,
63977
+ signal: null,
63060
63978
  lastUpdated: 0
63061
63979
  }
63062
63980
  },
@@ -63079,13 +63997,13 @@ onStatusChanged: { data: object({
63079
63997
  * History (D850): recorded series.
63080
63998
  */
63081
63999
  history: { fields: {
63082
- signalPercent: {
64000
+ signal: {
63083
64001
  kind: "gauge",
63084
64002
  unit: "%",
63085
64003
  deadband: 3,
63086
64004
  minIntervalMs: 6e4
63087
64005
  },
63088
- rssiDbm: {
64006
+ rssi: {
63089
64007
  kind: "gauge",
63090
64008
  unit: "dBm",
63091
64009
  deadband: 2,
@@ -63830,10 +64748,20 @@ var PetFeederDeviceStatusSchema = _enum([
63830
64748
  ]);
63831
64749
  var gramsPortion = number().int().min(4).max(200);
63832
64750
  var PetFeederStatusSchema = object({
63833
- /** Food currently in the bowl (grams). Null when the device has not
63834
- * reported a reading yet. On dual-hopper models this is the combined
63835
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
64751
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
64752
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
64753
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
64754
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
64755
+ * the device has not reported yet. */
63836
64756
  foodLevel: number().nullable().meta(readingMeta("none")),
64757
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
64758
+ * feeder's own units. Null when the model reports none. */
64759
+ bowlFood: number().nullable().meta(readingMeta("none")),
64760
+ /** True while the pet is eating (`state.eating`). */
64761
+ eating: boolean(),
64762
+ /** Whether `markFoodReplenished` does anything on this model. When false
64763
+ * the client must not offer it. */
64764
+ supportsMarkRefilled: boolean(),
63837
64765
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
63838
64766
  * single-hopper models. */
63839
64767
  food1: number().nullable().meta(readingMeta("none")),
@@ -63841,8 +64769,8 @@ var PetFeederStatusSchema = object({
63841
64769
  * single-hopper models. */
63842
64770
  food2: number().nullable().meta(readingMeta("none")),
63843
64771
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
63844
- * (`device_class: problem`, on = low). True when the bowl is empty /
63845
- * below the feeder's low threshold. */
64772
+ * (`device_class: problem`, on = low). True when the tank reports empty,
64773
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
63846
64774
  lowFood: boolean(),
63847
64775
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
63848
64776
  * device has no battery reading. */
@@ -63910,7 +64838,9 @@ var petFeederCapability = {
63910
64838
  kind: "mutation",
63911
64839
  auth: "protected"
63912
64840
  }),
63913
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
64841
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
64842
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
64843
+ * change any sensor reading. */
63914
64844
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
63915
64845
  kind: "mutation",
63916
64846
  auth: "protected"
@@ -63983,6 +64913,7 @@ var petFeederCapability = {
63983
64913
  */
63984
64914
  history: { fields: {
63985
64915
  foodLevel: { kind: "gauge" },
64916
+ bowlFood: { kind: "gauge" },
63986
64917
  food1: { kind: "gauge" },
63987
64918
  food2: { kind: "gauge" },
63988
64919
  batteryPower: {
@@ -64392,27 +65323,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
64392
65323
  * in real-world devices.
64393
65324
  */
64394
65325
  var PowerMeterStatusSchema = object({
64395
- /** Instantaneous power draw in watts. */
64396
- watts: number().optional().meta(readingMeta("power", { label: "Power" })),
65326
+ /** Instantaneous power draw (watts). */
65327
+ power: number().optional().meta(readingMeta("power", { label: "Power" })),
64397
65328
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64398
- kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64399
- /** Voltage in volts. */
64400
- volts: number().optional().meta(readingMeta("voltage")),
65329
+ energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
65330
+ /** Voltage (volts). */
65331
+ voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64401
65332
  /** Current in amperes. */
64402
- amps: number().optional().meta(readingMeta("current")),
65333
+ current: number().optional().meta(readingMeta("current", { label: "Current" })),
64403
65334
  /** Apparent power in volt-amperes. */
64404
- va: number().optional().meta(readingMeta("apparent-power")),
65335
+ apparentPower: number().optional().meta(readingMeta("apparent-power")),
64405
65336
  /** Power factor (0..1) if reported. */
64406
65337
  powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
64407
65338
  /** Ms epoch when the slice was last updated. */
64408
65339
  lastFetchedAt: number()
64409
65340
  });
65341
+ /**
65342
+ * Loads that draw metered mains power (D951). The reading is a feature of the
65343
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
65344
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
65345
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
65346
+ * camera and the like are left out: their draw is not what anyone meters.
65347
+ */
65348
+ var POWER_METER_DEVICE_TYPES = [
65349
+ DeviceType.Sensor,
65350
+ DeviceType.Switch,
65351
+ DeviceType.Light,
65352
+ DeviceType.Fan,
65353
+ DeviceType.Climate,
65354
+ DeviceType.Thermostat,
65355
+ DeviceType.Humidifier,
65356
+ DeviceType.WaterHeater,
65357
+ DeviceType.MediaPlayer,
65358
+ DeviceType.Washer,
65359
+ DeviceType.Dryer,
65360
+ DeviceType.Dishwasher,
65361
+ DeviceType.Vacuum,
65362
+ DeviceType.LawnMower,
65363
+ DeviceType.PetFeeder
65364
+ ];
65365
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
65366
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
65367
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
65368
+ function powerTarget(id, unit) {
65369
+ return {
65370
+ id,
65371
+ cardinality: "scalar",
65372
+ vocabulary: {
65373
+ cap: "power-meter",
65374
+ stateField: id
65375
+ },
65376
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
65377
+ apply: "add",
65378
+ path: id,
65379
+ sources: [{
65380
+ kind: "numeric",
65381
+ requiredUnit: unit,
65382
+ transforms: [{ kind: "identity" }]
65383
+ }],
65384
+ companions: [{
65385
+ kind: "clock",
65386
+ path: "lastFetchedAt",
65387
+ when: "always"
65388
+ }],
65389
+ sourceReport: { status: "assignment-status" },
65390
+ advancedExpression: true
65391
+ };
65392
+ }
65393
+ /** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
65394
+ var powerMeterCustomize = {
65395
+ version: 1,
65396
+ targets: [
65397
+ powerTarget("power", "W"),
65398
+ powerTarget("energy", "kWh"),
65399
+ powerTarget("voltage", "V"),
65400
+ powerTarget("current", "A")
65401
+ ]
65402
+ };
64410
65403
  var powerMeterCapability = {
64411
65404
  name: "power-meter",
64412
65405
  scope: "device",
64413
65406
  deviceNative: true,
64414
65407
  mode: "singleton",
64415
- deviceTypes: [DeviceType.Sensor],
65408
+ deviceTypes: POWER_METER_DEVICE_TYPES,
64416
65409
  methods: {},
64417
65410
  status: {
64418
65411
  schema: PowerMeterStatusSchema,
@@ -64420,41 +65413,41 @@ var powerMeterCapability = {
64420
65413
  },
64421
65414
  runtimeState: PowerMeterStatusSchema,
64422
65415
  /**
64423
- * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
65416
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
64424
65417
  *
64425
65418
  * See `RuntimeStateDurability`. Enforced by
64426
65419
  * `scripts/check-runtime-state-durability.ts`.
64427
65420
  */
64428
65421
  durability: "restored",
64429
65422
  /**
64430
- * History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
65423
+ * History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
64431
65424
  */
64432
65425
  history: { fields: {
64433
- watts: {
65426
+ power: {
64434
65427
  kind: "gauge",
64435
65428
  unit: "W",
64436
65429
  deadband: 1,
64437
65430
  minIntervalMs: 3e4
64438
65431
  },
64439
- kwhTotal: {
65432
+ energy: {
64440
65433
  kind: "counter",
64441
65434
  unit: "kWh",
64442
65435
  deadband: .01,
64443
65436
  minIntervalMs: 6e4
64444
65437
  },
64445
- volts: {
65438
+ voltage: {
64446
65439
  kind: "gauge",
64447
65440
  unit: "V",
64448
65441
  deadband: 1,
64449
65442
  minIntervalMs: 3e4
64450
65443
  },
64451
- amps: {
65444
+ current: {
64452
65445
  kind: "gauge",
64453
65446
  unit: "A",
64454
65447
  deadband: .05,
64455
65448
  minIntervalMs: 3e4
64456
65449
  },
64457
- va: {
65450
+ apparentPower: {
64458
65451
  kind: "gauge",
64459
65452
  unit: "VA",
64460
65453
  deadband: 1,
@@ -64469,7 +65462,8 @@ var powerMeterCapability = {
64469
65462
  } },
64470
65463
  /** Clock fields: written, but excluded from the compare that decides
64471
65464
  * whether persisting is worth a SQLite commit. */
64472
- volatileStateFields: ["lastFetchedAt"]
65465
+ volatileStateFields: ["lastFetchedAt"],
65466
+ customize: powerMeterCustomize
64473
65467
  };
64474
65468
  /**
64475
65469
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -64503,9 +65497,6 @@ var PresenceStatusSchema = object({
64503
65497
  location: string().nullable(),
64504
65498
  /** GPS coordinates when available. Null otherwise. */
64505
65499
  gps: GpsLocationSchema.nullable(),
64506
- /** Optional battery level of the tracking device (0..100). Null
64507
- * when not reported. */
64508
- batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64509
65500
  /** Ms epoch when the slice was last updated. */
64510
65501
  lastChangedAt: number()
64511
65502
  });
@@ -64534,14 +65525,7 @@ var presenceCapability = {
64534
65525
  * `scripts/check-runtime-state-durability.ts`.
64535
65526
  */
64536
65527
  durability: "restored",
64537
- /**
64538
- * History (D850): recorded series.
64539
- */
64540
- history: { fields: { batteryPercent: {
64541
- kind: "gauge",
64542
- unit: "%",
64543
- deadband: 1
64544
- } } },
65528
+ history: "none",
64545
65529
  /** Clock fields: written, but excluded from the compare that decides
64546
65530
  * whether persisting is worth a SQLite commit. */
64547
65531
  volatileStateFields: ["lastChangedAt"]
@@ -64556,7 +65540,7 @@ var presenceCapability = {
64556
65540
  */
64557
65541
  var PressureSensorStatusSchema = object({
64558
65542
  /** Current pressure in hPa. `null` when the sample was refused (D393). */
64559
- hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
65543
+ pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
64560
65544
  /** Ms epoch when the slice was last updated. */
64561
65545
  lastFetchedAt: number()
64562
65546
  });
@@ -64582,7 +65566,7 @@ var pressureSensorCapability = {
64582
65566
  /**
64583
65567
  * History (D850): recorded series.
64584
65568
  */
64585
- history: { fields: { hpa: {
65569
+ history: { fields: { pressure: {
64586
65570
  kind: "gauge",
64587
65571
  unit: "hPa",
64588
65572
  deadband: .1
@@ -65150,6 +66134,8 @@ var runningCustomize = {
65150
66134
  stateField: "running"
65151
66135
  },
65152
66136
  deviceTypes: [
66137
+ DeviceType.Washer,
66138
+ DeviceType.Dryer,
65153
66139
  DeviceType.Dishwasher,
65154
66140
  DeviceType.Vacuum,
65155
66141
  DeviceType.LawnMower,
@@ -67639,7 +68625,8 @@ var switchCapability = {
67639
68625
  deviceTypes: [
67640
68626
  DeviceType.Switch,
67641
68627
  DeviceType.Siren,
67642
- DeviceType.Light
68628
+ DeviceType.Light,
68629
+ DeviceType.Fan
67643
68630
  ],
67644
68631
  methods: { setState: method(object({
67645
68632
  deviceId: number(),
@@ -68278,7 +69265,7 @@ var tamperCapability = {
68278
69265
  */
68279
69266
  var TemperatureSensorStatusSchema = object({
68280
69267
  /** Current temperature in Celsius. `null` when the sample was refused (D393). */
68281
- celsius: number().nullable().meta(readingMeta("temperature", {
69268
+ temperature: number().nullable().meta(readingMeta("temperature", {
68282
69269
  precision: 1,
68283
69270
  label: "Temperature"
68284
69271
  })),
@@ -68307,7 +69294,7 @@ var temperatureSensorCapability = {
68307
69294
  /**
68308
69295
  * History (D850): recorded series.
68309
69296
  */
68310
- history: { fields: { celsius: {
69297
+ history: { fields: { temperature: {
68311
69298
  kind: "gauge",
68312
69299
  unit: "°C",
68313
69300
  deadband: .1
@@ -68604,7 +69591,7 @@ var VacuumControlStatusSchema = object({
68604
69591
  /** Speed tokens the hardware accepts — drives the UI selector. */
68605
69592
  availableFanSpeeds: array(string()),
68606
69593
  /** 0..100 cleaning-completion percentage of the current task, or null. */
68607
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
69594
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
68608
69595
  /** Current error code (0 / null = no error). */
68609
69596
  errorCode: number().nullable().meta(readingMeta("none")),
68610
69597
  /** Human label for {@link errorCode}, or null when none / undecodable. */
@@ -69852,6 +70839,7 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
69852
70839
  kind: "mutation",
69853
70840
  auth: "admin"
69854
70841
  });
70842
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
69855
70843
  /**
69856
70844
  * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
69857
70845
  * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
@@ -70236,7 +71224,7 @@ function tryConvertUnit(value, from, to) {
70236
71224
  return fromCanonical(toCanonical(value, fromSpec), toSpec);
70237
71225
  }
70238
71226
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
70239
- 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;
71227
+ 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;
70240
71228
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
70241
71229
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
70242
71230
  new Set(CAP_NAMES);
@@ -70565,6 +71553,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70565
71553
  featureProbe: featureProbeCapability,
70566
71554
  flood: floodCapability,
70567
71555
  gas: gasCapability,
71556
+ groupActive: groupActiveCapability,
70568
71557
  groupCommands: groupCommandsCapability,
70569
71558
  groupStatus: groupStatusCapability,
70570
71559
  heatingValve: heatingValveCapability,
@@ -70576,6 +71565,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70576
71565
  lawnMowerControl: lawnMowerControlCapability,
70577
71566
  lockControl: lockControlCapability,
70578
71567
  mediaPlayer: mediaPlayerCapability,
71568
+ meter: meterCapability,
70579
71569
  motion: motionCapability,
70580
71570
  motionTrigger: motionTriggerCapability,
70581
71571
  motionZones: motionZonesCapability,
@@ -70638,7 +71628,7 @@ var BaseDevice = class {
70638
71628
  * `device-local-state.ts`. Drivers access via:
70639
71629
  *
70640
71630
  * `this.state.battery.sleeping = true` // patches the battery slice
70641
- * `const pct = this.state.battery.percentage` // reads the battery slice
71631
+ * `const pct = this.state.battery.level` // reads the battery slice
70642
71632
  * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
70643
71633
  *
70644
71634
  * Adding a new cap with `runtimeState:` automatically extends this
@@ -70678,7 +71668,7 @@ var BaseDevice = class {
70678
71668
  * shape; drivers don't redeclare it, they just write through.
70679
71669
  *
70680
71670
  * Read: `this.runtimeState.getCapState('battery')` →
70681
- * `{percentage, charging, sleeping, lastUpdated}` for any
71671
+ * `{level, charging, sleeping, lastUpdated}` for any
70682
71672
  * provider that registers `batteryCapability`.
70683
71673
  * Write: `this.runtimeState.setCapState('battery', { … })`.
70684
71674
  *
@@ -71835,7 +72825,7 @@ var BaseDeviceProvider = class extends BaseAddon {
71835
72825
  return toDeviceSummary(device, this.addonId);
71836
72826
  }
71837
72827
  };
71838
- 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;
72828
+ 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;
71839
72829
  new Set(Object.values(DeviceType));
71840
72830
  Object.freeze({
71841
72831
  "accessories.getStatus": {
@@ -71852,6 +72842,104 @@ Object.freeze({
71852
72842
  access: "create",
71853
72843
  role: "admin"
71854
72844
  },
72845
+ "actions.create": {
72846
+ capName: "actions",
72847
+ capScope: "system",
72848
+ addonId: null,
72849
+ access: "create",
72850
+ role: "admin"
72851
+ },
72852
+ "actions.delete": {
72853
+ capName: "actions",
72854
+ capScope: "system",
72855
+ addonId: null,
72856
+ access: "delete",
72857
+ role: "admin"
72858
+ },
72859
+ "actions.get": {
72860
+ capName: "actions",
72861
+ capScope: "system",
72862
+ addonId: null,
72863
+ access: "view",
72864
+ role: "viewer"
72865
+ },
72866
+ "actions.getLinks": {
72867
+ capName: "actions",
72868
+ capScope: "system",
72869
+ addonId: null,
72870
+ access: "view",
72871
+ role: "admin"
72872
+ },
72873
+ "actions.getTraces": {
72874
+ capName: "actions",
72875
+ capScope: "system",
72876
+ addonId: null,
72877
+ access: "view",
72878
+ role: "admin"
72879
+ },
72880
+ "actions.list": {
72881
+ capName: "actions",
72882
+ capScope: "system",
72883
+ addonId: null,
72884
+ access: "view",
72885
+ role: "admin"
72886
+ },
72887
+ "actions.listForCamera": {
72888
+ capName: "actions",
72889
+ capScope: "system",
72890
+ addonId: null,
72891
+ access: "view",
72892
+ role: "viewer"
72893
+ },
72894
+ "actions.run": {
72895
+ capName: "actions",
72896
+ capScope: "system",
72897
+ addonId: null,
72898
+ access: "create",
72899
+ role: "viewer"
72900
+ },
72901
+ "actions.setLinks": {
72902
+ capName: "actions",
72903
+ capScope: "system",
72904
+ addonId: null,
72905
+ access: "create",
72906
+ role: "admin"
72907
+ },
72908
+ "actions.stop": {
72909
+ capName: "actions",
72910
+ capScope: "system",
72911
+ addonId: null,
72912
+ access: "create",
72913
+ role: "admin"
72914
+ },
72915
+ "actions.update": {
72916
+ capName: "actions",
72917
+ capScope: "system",
72918
+ addonId: null,
72919
+ access: "create",
72920
+ role: "admin"
72921
+ },
72922
+ "actionSource.listActions": {
72923
+ capName: "action-source",
72924
+ capScope: "system",
72925
+ addonId: null,
72926
+ access: "view",
72927
+ role: "admin"
72928
+ },
72929
+ "actionSource.runAction": {
72930
+ capName: "action-source",
72931
+ capScope: "system",
72932
+ addonId: null,
72933
+ access: "create",
72934
+ role: "admin"
72935
+ },
72936
+ "actionSource.stopAction": {
72937
+ capName: "action-source",
72938
+ capScope: "system",
72939
+ addonId: null,
72940
+ access: "create",
72941
+ role: "admin"
72942
+ },
71855
72943
  "addonPages.listPages": {
71856
72944
  capName: "addon-pages",
71857
72945
  capScope: "system",
@@ -73133,6 +74221,13 @@ Object.freeze({
73133
74221
  access: "view",
73134
74222
  role: "admin"
73135
74223
  },
74224
+ "coreBlocks.getAutomationRunState": {
74225
+ capName: "core-blocks",
74226
+ capScope: "system",
74227
+ addonId: null,
74228
+ access: "view",
74229
+ role: "admin"
74230
+ },
73136
74231
  "coreBlocks.getBlockSettings": {
73137
74232
  capName: "core-blocks",
73138
74233
  capScope: "system",
@@ -73154,6 +74249,13 @@ Object.freeze({
73154
74249
  access: "view",
73155
74250
  role: "admin"
73156
74251
  },
74252
+ "coreBlocks.getTraces": {
74253
+ capName: "core-blocks",
74254
+ capScope: "system",
74255
+ addonId: null,
74256
+ access: "view",
74257
+ role: "admin"
74258
+ },
73157
74259
  "coreBlocks.getTypeDefs": {
73158
74260
  capName: "core-blocks",
73159
74261
  capScope: "system",
@@ -73189,6 +74291,13 @@ Object.freeze({
73189
74291
  access: "create",
73190
74292
  role: "admin"
73191
74293
  },
74294
+ "coreBlocks.reportAutomationRun": {
74295
+ capName: "core-blocks",
74296
+ capScope: "system",
74297
+ addonId: null,
74298
+ access: "create",
74299
+ role: "admin"
74300
+ },
73192
74301
  "coreBlocks.restart": {
73193
74302
  capName: "core-blocks",
73194
74303
  capScope: "system",
@@ -75058,6 +76167,13 @@ Object.freeze({
75058
76167
  access: "view",
75059
76168
  role: "viewer"
75060
76169
  },
76170
+ "groupActive.getStatus": {
76171
+ capName: "group-active",
76172
+ capScope: "device",
76173
+ addonId: null,
76174
+ access: "view",
76175
+ role: "viewer"
76176
+ },
75061
76177
  "groupCommands.getStatus": {
75062
76178
  capName: "group-commands",
75063
76179
  capScope: "device",
@@ -76024,6 +77140,13 @@ Object.freeze({
76024
77140
  access: "create",
76025
77141
  role: "admin"
76026
77142
  },
77143
+ "meter.getStatus": {
77144
+ capName: "meter",
77145
+ capScope: "device",
77146
+ addonId: null,
77147
+ access: "view",
77148
+ role: "viewer"
77149
+ },
76027
77150
  "metricsProvider.collectSnapshot": {
76028
77151
  capName: "metrics-provider",
76029
77152
  capScope: "system",
@@ -81748,7 +82871,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81748
82871
  label: t("Full brightness", "Luminosità massima"),
81749
82872
  phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
81750
82873
  icon: "sun",
81751
- args: { percentage: 100 }
82874
+ args: { level: 100 }
81752
82875
  },
81753
82876
  {
81754
82877
  id: "brightness.half",
@@ -81756,7 +82879,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81756
82879
  label: t("Half brightness", "Luminosità a metà"),
81757
82880
  phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
81758
82881
  icon: "sun-dim",
81759
- args: { percentage: 50 }
82882
+ args: { level: 50 }
81760
82883
  },
81761
82884
  {
81762
82885
  id: "brightness.off",
@@ -81764,16 +82887,16 @@ var ACTUATOR_CAP_VOCABULARY = {
81764
82887
  label: t("Brightness off", "Luminosità a zero"),
81765
82888
  phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
81766
82889
  icon: "lightbulb-off",
81767
- args: { percentage: 0 }
82890
+ args: { level: 0 }
81768
82891
  },
81769
82892
  {
81770
82893
  id: "brightness.set",
81771
82894
  method: "setBrightness",
81772
82895
  label: t("Set brightness", "Imposta la luminosità"),
81773
- phrase: t("Set the brightness of {device} to {percentage}", "Imposta la luminosità di {device} a {percentage}"),
82896
+ phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
81774
82897
  icon: "sun-medium",
81775
82898
  openArgs: [{
81776
- key: "percentage",
82899
+ key: "level",
81777
82900
  label: PERCENT,
81778
82901
  offer: {
81779
82902
  kind: "number",
@@ -81787,13 +82910,13 @@ var ACTUATOR_CAP_VOCABULARY = {
81787
82910
  ],
81788
82911
  state: [{
81789
82912
  kind: "numeric",
81790
- field: "percentage",
82913
+ field: "level",
81791
82914
  quantity: t("the brightness", "la luminosità"),
81792
82915
  label: t("Brightness", "Luminosità"),
81793
82916
  unit: "%",
81794
82917
  icon: "sun"
81795
82918
  }],
81796
- events: { onBrightnessChanged: { notOperatorFacing: "the `percentage` state trigger reads the same change" } }
82919
+ events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
81797
82920
  },
81798
82921
  color: {
81799
82922
  scope: "device",
@@ -81807,7 +82930,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81807
82930
  icon: "sun",
81808
82931
  args: { color: {
81809
82932
  mode: "mired",
81810
- mireds: 370
82933
+ colorTemperature: 370
81811
82934
  } }
81812
82935
  },
81813
82936
  {
@@ -81818,7 +82941,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81818
82941
  icon: "snowflake",
81819
82942
  args: { color: {
81820
82943
  mode: "mired",
81821
- mireds: 200
82944
+ colorTemperature: 200
81822
82945
  } }
81823
82946
  },
81824
82947
  {
@@ -82432,7 +83555,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82432
83555
  label: t("Fan off", "Ventilatore spento"),
82433
83556
  phrase: t("Stop {device}", "Ferma {device}"),
82434
83557
  icon: "power-off",
82435
- args: { percentage: 0 }
83558
+ args: { speed: 0 }
82436
83559
  },
82437
83560
  {
82438
83561
  id: "fan.full",
@@ -82440,16 +83563,16 @@ var ACTUATOR_CAP_VOCABULARY = {
82440
83563
  label: t("Fan full", "Ventilatore al massimo"),
82441
83564
  phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
82442
83565
  icon: "fan",
82443
- args: { percentage: 100 }
83566
+ args: { speed: 100 }
82444
83567
  },
82445
83568
  {
82446
83569
  id: "fan.speed",
82447
83570
  method: "setPercentage",
82448
83571
  label: t("Set speed", "Imposta la velocità"),
82449
- phrase: t("Set the speed of {device} to {percentage}", "Imposta la velocità di {device} al {percentage}"),
83572
+ phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
82450
83573
  icon: "fan",
82451
83574
  openArgs: [{
82452
- key: "percentage",
83575
+ key: "speed",
82453
83576
  label: t("Speed", "Velocità"),
82454
83577
  offer: {
82455
83578
  kind: "number",
@@ -82511,7 +83634,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82511
83634
  ],
82512
83635
  state: [{
82513
83636
  kind: "numeric",
82514
- field: "percentage",
83637
+ field: "speed",
82515
83638
  quantity: t("the speed", "la velocità"),
82516
83639
  label: t("Speed", "Velocità"),
82517
83640
  unit: "%",
@@ -83309,7 +84432,7 @@ var APPLIANCE_CAP_VOCABULARY = {
83309
84432
  },
83310
84433
  {
83311
84434
  kind: "numeric",
83312
- field: "progressPercent",
84435
+ field: "progress",
83313
84436
  quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
83314
84437
  label: t("Progress", "Avanzamento"),
83315
84438
  unit: "%",
@@ -83667,10 +84790,10 @@ var APPLIANCE_CAP_VOCABULARY = {
83667
84790
  id: "media.seek",
83668
84791
  method: "seek",
83669
84792
  label: t("Seek", "Vai al punto"),
83670
- phrase: t("Move {device} to {positionMs}", "Porta {device} al punto {positionMs}"),
84793
+ phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
83671
84794
  icon: "fast-forward",
83672
84795
  openArgs: [{
83673
- key: "positionMs",
84796
+ key: "position",
83674
84797
  label: t("Position", "Posizione"),
83675
84798
  offer: {
83676
84799
  kind: "number",
@@ -84122,7 +85245,39 @@ var APPLIANCE_CAP_VOCABULARY = {
84122
85245
  phrase: t("When {device} finishes", "Quando {device} finisce"),
84123
85246
  icon: "square"
84124
85247
  }
84125
- }
85248
+ },
85249
+ overrides: { byType: {
85250
+ [DeviceType.Washer]: {
85251
+ state: { running: {
85252
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
85253
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
85254
+ } },
85255
+ events: {
85256
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
85257
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
85258
+ }
85259
+ },
85260
+ [DeviceType.Dryer]: {
85261
+ state: { running: {
85262
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
85263
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
85264
+ } },
85265
+ events: {
85266
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
85267
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
85268
+ }
85269
+ },
85270
+ [DeviceType.Dishwasher]: {
85271
+ state: { running: {
85272
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
85273
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
85274
+ } },
85275
+ events: {
85276
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
85277
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
85278
+ }
85279
+ }
85280
+ } }
84126
85281
  },
84127
85282
  update: {
84128
85283
  scope: "device",
@@ -84687,7 +85842,7 @@ var SENSOR_CAP_VOCABULARY = {
84687
85842
  state: [
84688
85843
  {
84689
85844
  kind: "numeric",
84690
- field: "co2Ppm",
85845
+ field: "co2",
84691
85846
  quantity: t("the CO₂", "la CO₂"),
84692
85847
  label: t("CO₂", "CO₂"),
84693
85848
  unit: "ppm",
@@ -84695,7 +85850,7 @@ var SENSOR_CAP_VOCABULARY = {
84695
85850
  },
84696
85851
  {
84697
85852
  kind: "numeric",
84698
- field: "vocPpb",
85853
+ field: "voc",
84699
85854
  quantity: t("the VOC level", "il livello di COV"),
84700
85855
  label: t("VOC", "COV"),
84701
85856
  unit: "ppb",
@@ -84731,7 +85886,7 @@ var SENSOR_CAP_VOCABULARY = {
84731
85886
  noun: t("Light level", "Luminosità ambiente"),
84732
85887
  state: [{
84733
85888
  kind: "numeric",
84734
- field: "lux",
85889
+ field: "illuminance",
84735
85890
  quantity: t("the light level", "la luminosità"),
84736
85891
  label: t("Illuminance", "Illuminamento"),
84737
85892
  unit: "lx",
@@ -84894,6 +86049,10 @@ var SENSOR_CAP_VOCABULARY = {
84894
86049
  icon: "flame"
84895
86050
  }]
84896
86051
  },
86052
+ "group-active": {
86053
+ scope: "device",
86054
+ noun: t("Group state", "Stato del gruppo")
86055
+ },
84897
86056
  "group-commands": {
84898
86057
  scope: "device",
84899
86058
  noun: t("Group command", "Comando di gruppo")
@@ -84930,7 +86089,7 @@ var SENSOR_CAP_VOCABULARY = {
84930
86089
  noun: t("Humidity", "Umidità"),
84931
86090
  state: [{
84932
86091
  kind: "numeric",
84933
- field: "percent",
86092
+ field: "humidity",
84934
86093
  quantity: t("the humidity", "l’umidità"),
84935
86094
  label: t("Humidity", "Umidità"),
84936
86095
  unit: "%",
@@ -84950,6 +86109,17 @@ var SENSOR_CAP_VOCABULARY = {
84950
86109
  }],
84951
86110
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
84952
86111
  },
86112
+ meter: {
86113
+ scope: "device",
86114
+ noun: t("Meter", "Contatore"),
86115
+ state: [{
86116
+ kind: "numeric",
86117
+ field: "value",
86118
+ quantity: t("the total", "il totale"),
86119
+ label: t("Total", "Totale"),
86120
+ icon: "gauge"
86121
+ }]
86122
+ },
84953
86123
  "numeric-sensor": {
84954
86124
  scope: "device",
84955
86125
  noun: t("Sensor", "Sensore"),
@@ -84967,7 +86137,7 @@ var SENSOR_CAP_VOCABULARY = {
84967
86137
  state: [
84968
86138
  {
84969
86139
  kind: "numeric",
84970
- field: "watts",
86140
+ field: "power",
84971
86141
  quantity: t("the power draw", "il consumo"),
84972
86142
  label: t("Power", "Potenza"),
84973
86143
  unit: "W",
@@ -84975,7 +86145,7 @@ var SENSOR_CAP_VOCABULARY = {
84975
86145
  },
84976
86146
  {
84977
86147
  kind: "numeric",
84978
- field: "kwhTotal",
86148
+ field: "energy",
84979
86149
  quantity: t("the energy used", "l’energia consumata"),
84980
86150
  label: t("Energy", "Energia"),
84981
86151
  unit: "kWh",
@@ -84983,7 +86153,7 @@ var SENSOR_CAP_VOCABULARY = {
84983
86153
  },
84984
86154
  {
84985
86155
  kind: "numeric",
84986
- field: "volts",
86156
+ field: "voltage",
84987
86157
  quantity: t("the voltage", "la tensione"),
84988
86158
  label: t("Voltage", "Tensione"),
84989
86159
  unit: "V",
@@ -84991,7 +86161,7 @@ var SENSOR_CAP_VOCABULARY = {
84991
86161
  },
84992
86162
  {
84993
86163
  kind: "numeric",
84994
- field: "amps",
86164
+ field: "current",
84995
86165
  quantity: t("the current", "la corrente"),
84996
86166
  label: t("Current", "Corrente"),
84997
86167
  unit: "A",
@@ -85008,13 +86178,6 @@ var SENSOR_CAP_VOCABULARY = {
85008
86178
  label: t("Where", "Posizione"),
85009
86179
  phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
85010
86180
  icon: "map-pin"
85011
- }, {
85012
- kind: "numeric",
85013
- field: "batteryPercent",
85014
- quantity: t("the phone battery", "la batteria del telefono"),
85015
- label: t("Battery", "Batteria"),
85016
- unit: "%",
85017
- icon: "battery"
85018
86181
  }]
85019
86182
  },
85020
86183
  "pressure-sensor": {
@@ -85022,7 +86185,7 @@ var SENSOR_CAP_VOCABULARY = {
85022
86185
  noun: t("Pressure", "Pressione"),
85023
86186
  state: [{
85024
86187
  kind: "numeric",
85025
- field: "hpa",
86188
+ field: "pressure",
85026
86189
  quantity: t("the pressure", "la pressione"),
85027
86190
  label: t("Pressure", "Pressione"),
85028
86191
  unit: "hPa",
@@ -85058,7 +86221,7 @@ var SENSOR_CAP_VOCABULARY = {
85058
86221
  noun: t("Temperature", "Temperatura"),
85059
86222
  state: [{
85060
86223
  kind: "numeric",
85061
- field: "celsius",
86224
+ field: "temperature",
85062
86225
  quantity: t("the temperature", "la temperatura"),
85063
86226
  label: t("Temperature", "Temperatura"),
85064
86227
  unit: "°C",
@@ -85129,7 +86292,7 @@ var SENSOR_CAP_VOCABULARY = {
85129
86292
  state: [
85130
86293
  {
85131
86294
  kind: "numeric",
85132
- field: "percentage",
86295
+ field: "level",
85133
86296
  quantity: t("the battery level", "il livello della batteria"),
85134
86297
  label: t("Battery", "Batteria"),
85135
86298
  unit: "%",
@@ -85171,7 +86334,7 @@ var SENSOR_CAP_VOCABULARY = {
85171
86334
  icon: "battery-charging"
85172
86335
  }
85173
86336
  ],
85174
- events: { onStatusChanged: { notOperatorFacing: "the percentage / sleeping / charging state triggers each read one field of this change" } },
86337
+ events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
85175
86338
  extraEvents: [{
85176
86339
  category: EventCategory.DeviceAwake,
85177
86340
  label: t("Woke up", "Risvegliato"),
@@ -85189,7 +86352,7 @@ var SENSOR_CAP_VOCABULARY = {
85189
86352
  noun: t("Network link", "Collegamento di rete"),
85190
86353
  state: [{
85191
86354
  kind: "numeric",
85192
- field: "signalPercent",
86355
+ field: "signal",
85193
86356
  quantity: t("the signal strength", "l’intensità del segnale"),
85194
86357
  label: t("Signal", "Segnale"),
85195
86358
  unit: "%",
@@ -85282,6 +86445,8 @@ var ML = "model and training machinery — no device action";
85282
86445
  var INTERNAL = "internal plumbing between addons — never operator-facing";
85283
86446
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
85284
86447
  var SYSTEM_CAP_VOCABULARY = {
86448
+ "action-source": system(INTERNAL),
86449
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
85285
86450
  "addon-pages": system(UI_PLUMBING),
85286
86451
  "addon-pages-source": system(UI_PLUMBING),
85287
86452
  "addon-routes": system(UI_PLUMBING),
@@ -85422,8 +86587,47 @@ function projectAction(cap, action) {
85422
86587
  };
85423
86588
  }
85424
86589
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
85425
- 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;
86590
+ 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;
85426
86591
  DeviceFeature.BatteryOperated;
86592
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
86593
+ 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.";
86594
+ 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.";
86595
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
86596
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
86597
+ 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.";
86598
+ function withheld(reason) {
86599
+ return {
86600
+ [DeviceType.Washer]: {
86601
+ exported: false,
86602
+ reason
86603
+ },
86604
+ [DeviceType.Dryer]: {
86605
+ exported: false,
86606
+ reason
86607
+ },
86608
+ [DeviceType.Dishwasher]: {
86609
+ exported: false,
86610
+ reason
86611
+ }
86612
+ };
86613
+ }
86614
+ function offered(received) {
86615
+ return {
86616
+ [DeviceType.Washer]: {
86617
+ exported: true,
86618
+ as: received
86619
+ },
86620
+ [DeviceType.Dryer]: {
86621
+ exported: true,
86622
+ as: received
86623
+ },
86624
+ [DeviceType.Dishwasher]: {
86625
+ exported: true,
86626
+ as: received
86627
+ }
86628
+ };
86629
+ }
86630
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
85427
86631
  Object.freeze({
85428
86632
  "accessories.getStatus": [{
85429
86633
  name: "deviceId",
@@ -85439,6 +86643,31 @@ Object.freeze({
85439
86643
  form: "single",
85440
86644
  optional: false
85441
86645
  }],
86646
+ "actions.get": [{
86647
+ name: "deviceId",
86648
+ form: "single",
86649
+ optional: true
86650
+ }],
86651
+ "actions.getLinks": [{
86652
+ name: "deviceId",
86653
+ form: "single",
86654
+ optional: false
86655
+ }],
86656
+ "actions.listForCamera": [{
86657
+ name: "deviceId",
86658
+ form: "single",
86659
+ optional: false
86660
+ }],
86661
+ "actions.run": [{
86662
+ name: "deviceId",
86663
+ form: "single",
86664
+ optional: true
86665
+ }],
86666
+ "actions.setLinks": [{
86667
+ name: "deviceId",
86668
+ form: "single",
86669
+ optional: false
86670
+ }],
85442
86671
  "addonSettings.getDeviceSettings": [{
85443
86672
  name: "deviceId",
85444
86673
  form: "single",
@@ -86390,6 +87619,11 @@ Object.freeze({
86390
87619
  form: "single",
86391
87620
  optional: false
86392
87621
  }],
87622
+ "groupActive.getStatus": [{
87623
+ name: "deviceId",
87624
+ form: "single",
87625
+ optional: false
87626
+ }],
86393
87627
  "groupCommands.getStatus": [{
86394
87628
  name: "deviceId",
86395
87629
  form: "single",
@@ -86600,6 +87834,11 @@ Object.freeze({
86600
87834
  form: "single",
86601
87835
  optional: false
86602
87836
  }],
87837
+ "meter.getStatus": [{
87838
+ name: "deviceId",
87839
+ form: "single",
87840
+ optional: false
87841
+ }],
86603
87842
  "motion.getStatus": [{
86604
87843
  name: "deviceId",
86605
87844
  form: "single",
@@ -88207,7 +89446,6 @@ Object.freeze({
88207
89446
  "network-access": "ingress",
88208
89447
  "smtp-provider": "email"
88209
89448
  });
88210
- DeviceType.Camera;
88211
89449
  /**
88212
89450
  * A saved Customize assignment (D936). The schema is the type: callers use
88213
89451
  * `z.infer`, they do not restate the shape.
@@ -89653,6 +90891,7 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
89653
90891
  }
89654
90892
  return best;
89655
90893
  }
90894
+ String.raw`(?<=^|["'(\s>,\[{])`;
89656
90895
  var MB = 1024 * 1024;
89657
90896
  1024 * MB, 3072 * MB;
89658
90897
  //#endregion
@@ -115005,15 +116244,15 @@ var HmActuatorDevice = class extends HmChildDevice {
115005
116244
  * keep that class a thin actuator-wiring layer.
115006
116245
  */
115007
116246
  var COLD_TEMPERATURE = {
115008
- celsius: 0,
116247
+ temperature: 0,
115009
116248
  lastFetchedAt: 0
115010
116249
  };
115011
116250
  var COLD_HUMIDITY = {
115012
- percent: 0,
116251
+ humidity: 0,
115013
116252
  lastFetchedAt: 0
115014
116253
  };
115015
116254
  var COLD_AMBIENT_LIGHT = {
115016
- lux: 0,
116255
+ illuminance: 0,
115017
116256
  lastFetchedAt: 0
115018
116257
  };
115019
116258
  var COLD_POWER = { lastFetchedAt: 0 };
@@ -115105,15 +116344,15 @@ var HmSensorDevice = class extends HmChildDevice {
115105
116344
  decodeSensorPush(parameter, value, now) {
115106
116345
  if (typeof value !== "number") return;
115107
116346
  if (this.caps.has("temperature-sensor") && parameter === "ACTUAL_TEMPERATURE") this.runtimeState.setCapState("temperature-sensor", {
115108
- celsius: value,
116347
+ temperature: value,
115109
116348
  lastFetchedAt: now
115110
116349
  });
115111
116350
  if (this.caps.has("humidity-sensor") && parameter === "HUMIDITY") this.runtimeState.setCapState("humidity-sensor", {
115112
- percent: value,
116351
+ humidity: value,
115113
116352
  lastFetchedAt: now
115114
116353
  });
115115
116354
  if (this.caps.has("ambient-light-sensor") && parameter === "ILLUMINATION") this.runtimeState.setCapState("ambient-light-sensor", {
115116
- lux: Math.max(0, value),
116355
+ illuminance: Math.max(0, value),
115117
116356
  lastFetchedAt: now
115118
116357
  });
115119
116358
  this.decodePowerPush(parameter, value, now);
@@ -115126,28 +116365,28 @@ var HmSensorDevice = class extends HmChildDevice {
115126
116365
  case "POWER":
115127
116366
  this.runtimeState.setCapState("power-meter", {
115128
116367
  ...prev,
115129
- watts: value,
116368
+ power: value,
115130
116369
  lastFetchedAt: now
115131
116370
  });
115132
116371
  return;
115133
116372
  case "ENERGY_COUNTER":
115134
116373
  this.runtimeState.setCapState("power-meter", {
115135
116374
  ...prev,
115136
- kwhTotal: value / 1e3,
116375
+ energy: value / 1e3,
115137
116376
  lastFetchedAt: now
115138
116377
  });
115139
116378
  return;
115140
116379
  case "VOLTAGE":
115141
116380
  this.runtimeState.setCapState("power-meter", {
115142
116381
  ...prev,
115143
- volts: value,
116382
+ voltage: value,
115144
116383
  lastFetchedAt: now
115145
116384
  });
115146
116385
  return;
115147
116386
  case "CURRENT":
115148
116387
  this.runtimeState.setCapState("power-meter", {
115149
116388
  ...prev,
115150
- amps: value / 1e3,
116389
+ current: value / 1e3,
115151
116390
  lastFetchedAt: now
115152
116391
  });
115153
116392
  return;
@@ -115245,7 +116484,7 @@ var COLD_BINARY = {
115245
116484
  lastChangedAt: 0
115246
116485
  };
115247
116486
  var COLD_TEMP = {
115248
- celsius: 0,
116487
+ temperature: 0,
115249
116488
  lastFetchedAt: 0
115250
116489
  };
115251
116490
  /**
@@ -115309,7 +116548,7 @@ var HmDiagnosticDevice = class extends HmChildDevice {
115309
116548
  if (stored === null) return;
115310
116549
  if (this.capName === "temperature-sensor") {
115311
116550
  this.runtimeState.setCapState("temperature-sensor", {
115312
- celsius: stored,
116551
+ temperature: stored,
115313
116552
  lastFetchedAt: now
115314
116553
  });
115315
116554
  return;