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