@camstack/addon-provider-reolink 1.2.211 → 1.2.213

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.mjs CHANGED
@@ -28,7 +28,7 @@ import { URL as URL$1 } from "url";
28
28
  import { spawn as spawn$1 } from "node:child_process";
29
29
  import os, { tmpdir } from "node:os";
30
30
  import { createReadStream } from "node:fs";
31
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
31
+ //#region ../types/dist/event-category-DmZDPDf_.mjs
32
32
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
33
33
  EventCategory["SystemBoot"] = "system.boot";
34
34
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -655,7 +655,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
655
655
  * (a track was promoted to a stationary-registry entry) or departs (the
656
656
  * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
657
657
  * the dedicated "Stationary" section — never the live event feed. Payload:
658
- * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
658
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
659
659
  */
660
660
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
661
661
  /**
@@ -793,6 +793,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
793
793
  */
794
794
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
795
795
  /**
796
+ * An action run started or ended. Emitted by the `actions` surface.
797
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
798
+ */
799
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
800
+ /**
796
801
  * Progress update from a running model conversion job.
797
802
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
798
803
  * Emitted by `addon-model-studio` on the converting node.
@@ -5471,7 +5476,7 @@ var ZodIssueCode = {
5471
5476
  var ZodFirstPartyTypeKind;
5472
5477
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5473
5478
  //#endregion
5474
- //#region ../types/dist/sleep-bykKrTgu.mjs
5479
+ //#region ../types/dist/sleep-Crlmu_tO.mjs
5475
5480
  /**
5476
5481
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5477
5482
  * window to float samples (D455).
@@ -8629,6 +8634,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
8629
8634
  /** Every capability name, so addon code can check a name without the schema graph. */
8630
8635
  var CAP_NAMES = Object.freeze([
8631
8636
  "accessories",
8637
+ "action-source",
8638
+ "actions",
8632
8639
  "addon-pages",
8633
8640
  "addon-pages-source",
8634
8641
  "addon-routes",
@@ -8702,6 +8709,7 @@ var CAP_NAMES = Object.freeze([
8702
8709
  "filesystem-browse",
8703
8710
  "flood",
8704
8711
  "gas",
8712
+ "group-active",
8705
8713
  "group-commands",
8706
8714
  "group-status",
8707
8715
  "heating-valve",
@@ -8724,6 +8732,7 @@ var CAP_NAMES = Object.freeze([
8724
8732
  "login-method",
8725
8733
  "media-player",
8726
8734
  "mesh-network",
8735
+ "meter",
8727
8736
  "metrics-provider",
8728
8737
  "model-convert",
8729
8738
  "model-distributor",
@@ -8815,11 +8824,11 @@ var CAP_NAMES = Object.freeze([
8815
8824
  Object.freeze({
8816
8825
  "air-quality-sensor": [
8817
8826
  {
8818
- "path": "co2Ppm",
8827
+ "path": "co2",
8819
8828
  "valueType": "number"
8820
8829
  },
8821
8830
  {
8822
- "path": "vocPpb",
8831
+ "path": "voc",
8823
8832
  "valueType": "number"
8824
8833
  },
8825
8834
  {
@@ -8869,7 +8878,7 @@ Object.freeze({
8869
8878
  }
8870
8879
  ],
8871
8880
  "ambient-light-sensor": [{
8872
- "path": "lux",
8881
+ "path": "illuminance",
8873
8882
  "valueType": "number"
8874
8883
  }],
8875
8884
  "audio-metrics": [
@@ -8930,7 +8939,7 @@ Object.freeze({
8930
8939
  ],
8931
8940
  "battery": [
8932
8941
  {
8933
- "path": "percentage",
8942
+ "path": "level",
8934
8943
  "valueType": "number"
8935
8944
  },
8936
8945
  {
@@ -8952,7 +8961,7 @@ Object.freeze({
8952
8961
  "valueType": "number"
8953
8962
  },
8954
8963
  {
8955
- "path": "volts",
8964
+ "path": "voltage",
8956
8965
  "valueType": "number"
8957
8966
  },
8958
8967
  {
@@ -8973,7 +8982,7 @@ Object.freeze({
8973
8982
  "valueType": "boolean"
8974
8983
  }],
8975
8984
  "brightness": [{
8976
- "path": "percentage",
8985
+ "path": "level",
8977
8986
  "valueType": "number"
8978
8987
  }],
8979
8988
  "broker": [{
@@ -9174,7 +9183,7 @@ Object.freeze({
9174
9183
  "valueType": "number"
9175
9184
  },
9176
9185
  {
9177
- "path": "mireds",
9186
+ "path": "colorTemperature",
9178
9187
  "valueType": "number"
9179
9188
  }
9180
9189
  ],
@@ -9353,15 +9362,15 @@ Object.freeze({
9353
9362
  "valueType": "string"
9354
9363
  },
9355
9364
  {
9356
- "path": "remainingSeconds",
9365
+ "path": "remaining",
9357
9366
  "valueType": "number"
9358
9367
  },
9359
9368
  {
9360
- "path": "delayedStartSeconds",
9369
+ "path": "delayedStart",
9361
9370
  "valueType": "number"
9362
9371
  },
9363
9372
  {
9364
- "path": "progressPercent",
9373
+ "path": "progress",
9365
9374
  "valueType": "number"
9366
9375
  },
9367
9376
  {
@@ -9475,7 +9484,7 @@ Object.freeze({
9475
9484
  ],
9476
9485
  "fan-control": [
9477
9486
  {
9478
- "path": "percentage",
9487
+ "path": "speed",
9479
9488
  "valueType": "number"
9480
9489
  },
9481
9490
  {
@@ -9522,6 +9531,10 @@ Object.freeze({
9522
9531
  "path": "detected",
9523
9532
  "valueType": "boolean"
9524
9533
  }],
9534
+ "group-active": [{
9535
+ "path": "active",
9536
+ "valueType": "boolean"
9537
+ }],
9525
9538
  "group-status": [
9526
9539
  {
9527
9540
  "path": "included",
@@ -9571,7 +9584,7 @@ Object.freeze({
9571
9584
  }
9572
9585
  ],
9573
9586
  "humidity-sensor": [{
9574
- "path": "percent",
9587
+ "path": "humidity",
9575
9588
  "valueType": "number"
9576
9589
  }],
9577
9590
  "image": [{
@@ -9681,7 +9694,7 @@ Object.freeze({
9681
9694
  "valueType": "number"
9682
9695
  },
9683
9696
  {
9684
- "path": "progressPercent",
9697
+ "path": "progress",
9685
9698
  "valueType": "number"
9686
9699
  },
9687
9700
  {
@@ -9760,11 +9773,11 @@ Object.freeze({
9760
9773
  "valueType": "string"
9761
9774
  },
9762
9775
  {
9763
- "path": "positionMs",
9776
+ "path": "position",
9764
9777
  "valueType": "number"
9765
9778
  },
9766
9779
  {
9767
- "path": "durationMs",
9780
+ "path": "duration",
9768
9781
  "valueType": "number"
9769
9782
  },
9770
9783
  {
@@ -9781,6 +9794,10 @@ Object.freeze({
9781
9794
  ]
9782
9795
  }
9783
9796
  ],
9797
+ "meter": [{
9798
+ "path": "value",
9799
+ "valueType": "number"
9800
+ }],
9784
9801
  "motion": [
9785
9802
  {
9786
9803
  "path": "detected",
@@ -9862,11 +9879,11 @@ Object.freeze({
9862
9879
  ]
9863
9880
  },
9864
9881
  {
9865
- "path": "signalPercent",
9882
+ "path": "signal",
9866
9883
  "valueType": "number"
9867
9884
  },
9868
9885
  {
9869
- "path": "rssiDbm",
9886
+ "path": "rssi",
9870
9887
  "valueType": "number"
9871
9888
  },
9872
9889
  {
@@ -9921,6 +9938,18 @@ Object.freeze({
9921
9938
  "path": "foodLevel",
9922
9939
  "valueType": "number"
9923
9940
  },
9941
+ {
9942
+ "path": "bowlFood",
9943
+ "valueType": "number"
9944
+ },
9945
+ {
9946
+ "path": "eating",
9947
+ "valueType": "boolean"
9948
+ },
9949
+ {
9950
+ "path": "supportsMarkRefilled",
9951
+ "valueType": "boolean"
9952
+ },
9924
9953
  {
9925
9954
  "path": "food1",
9926
9955
  "valueType": "number"
@@ -9985,23 +10014,23 @@ Object.freeze({
9985
10014
  ],
9986
10015
  "power-meter": [
9987
10016
  {
9988
- "path": "watts",
10017
+ "path": "power",
9989
10018
  "valueType": "number"
9990
10019
  },
9991
10020
  {
9992
- "path": "kwhTotal",
10021
+ "path": "energy",
9993
10022
  "valueType": "number"
9994
10023
  },
9995
10024
  {
9996
- "path": "volts",
10025
+ "path": "voltage",
9997
10026
  "valueType": "number"
9998
10027
  },
9999
10028
  {
10000
- "path": "amps",
10029
+ "path": "current",
10001
10030
  "valueType": "number"
10002
10031
  },
10003
10032
  {
10004
- "path": "va",
10033
+ "path": "apparentPower",
10005
10034
  "valueType": "number"
10006
10035
  },
10007
10036
  {
@@ -10029,14 +10058,10 @@ Object.freeze({
10029
10058
  {
10030
10059
  "path": "gps.accuracyMeters",
10031
10060
  "valueType": "number"
10032
- },
10033
- {
10034
- "path": "batteryPercent",
10035
- "valueType": "number"
10036
10061
  }
10037
10062
  ],
10038
10063
  "pressure-sensor": [{
10039
- "path": "hpa",
10064
+ "path": "pressure",
10040
10065
  "valueType": "number"
10041
10066
  }],
10042
10067
  "privacy-mask": [{
@@ -10350,7 +10375,7 @@ Object.freeze({
10350
10375
  "valueType": "boolean"
10351
10376
  }],
10352
10377
  "temperature-sensor": [{
10353
- "path": "celsius",
10378
+ "path": "temperature",
10354
10379
  "valueType": "number"
10355
10380
  }],
10356
10381
  "update": [
@@ -10398,7 +10423,7 @@ Object.freeze({
10398
10423
  "valueType": "string"
10399
10424
  },
10400
10425
  {
10401
- "path": "progressPercent",
10426
+ "path": "progress",
10402
10427
  "valueType": "number"
10403
10428
  },
10404
10429
  {
@@ -10562,7 +10587,7 @@ function sleep$2(ms) {
10562
10587
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
10563
10588
  }
10564
10589
  //#endregion
10565
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10590
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10566
10591
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10567
10592
  DeviceType["Camera"] = "camera";
10568
10593
  DeviceType["Hub"] = "hub";
@@ -10656,8 +10681,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10656
10681
  DeviceType["PetFeeder"] = "pet-feeder";
10657
10682
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10658
10683
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10659
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10684
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10685
+ * A plug-built dishwasher carries running-state and power instead. */
10660
10686
  DeviceType["Dishwasher"] = "dishwasher";
10687
+ /** Washing machine. A plug-built one is running while its power stays
10688
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10689
+ DeviceType["Washer"] = "washer";
10690
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10691
+ DeviceType["Dryer"] = "dryer";
10661
10692
  return DeviceType;
10662
10693
  }({});
10663
10694
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -10905,7 +10936,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
10905
10936
  return DeviceRole;
10906
10937
  }({});
10907
10938
  //#endregion
10908
- //#region ../types/dist/composition-OhpuespA.mjs
10939
+ //#region ../types/dist/composition-CZImLpOI.mjs
10909
10940
  /**
10910
10941
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10911
10942
  *
@@ -11220,6 +11251,13 @@ var table$1 = {
11220
11251
  apply: () => {
11221
11252
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11222
11253
  }
11254
+ },
11255
+ schmitt: {
11256
+ minArgs: 3,
11257
+ maxArgs: 5,
11258
+ apply: () => {
11259
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11260
+ }
11223
11261
  }
11224
11262
  };
11225
11263
  Object.freeze(Object.assign(Object.create(null), table$1));
@@ -11661,6 +11699,99 @@ function validateExpressionSource(src) {
11661
11699
  return null;
11662
11700
  }
11663
11701
  /**
11702
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11703
+ *
11704
+ * Standby of the control board is under 5 W; a motor or a heater is far
11705
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11706
+ * a cycle, so running starts only after 30 s above the line.
11707
+ *
11708
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11709
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11710
+ * report "finished" in the middle of the program, so the stop dwell is
11711
+ * 10 minutes on every type. The number is stored per type so one machine
11712
+ * can move later without moving the others.
11713
+ */
11714
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11715
+ var APPLIANCE_KINDS = [
11716
+ "washer",
11717
+ "dryer",
11718
+ "dishwasher"
11719
+ ];
11720
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11721
+ /**
11722
+ * Statistics and integrals over a history read (D944).
11723
+ *
11724
+ * Pure: the same points always give the same answer, so a restart recomputes
11725
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11726
+ * never the mean of the samples. A hole with no held value is not integrated
11727
+ * as zero. A truncated read is not published.
11728
+ */
11729
+ var STATISTIC_STATS = [
11730
+ "min",
11731
+ "max",
11732
+ "mean",
11733
+ "sum",
11734
+ "count",
11735
+ "change"
11736
+ ];
11737
+ /**
11738
+ * The range a statistics or integral helper asks history for (D944).
11739
+ *
11740
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11741
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11742
+ * is strictly longer than the longest that range can be. Forever qualifies.
11743
+ * Equal to a tier's retention is a refusal, not a short answer.
11744
+ */
11745
+ var MINUTE_MS = 6e4;
11746
+ var HOUR_MS = 60 * MINUTE_MS;
11747
+ var DAY_MS = 24 * HOUR_MS;
11748
+ var ROLLING_SPANS = [
11749
+ "1h",
11750
+ "24h",
11751
+ "2d",
11752
+ "7d",
11753
+ "30d",
11754
+ "custom"
11755
+ ];
11756
+ var CALENDAR_PERIODS = [
11757
+ "today",
11758
+ "yesterday",
11759
+ "this-week",
11760
+ "this-month"
11761
+ ];
11762
+ var RESET_PERIODS = [
11763
+ "never",
11764
+ "daily",
11765
+ "weekly",
11766
+ "monthly"
11767
+ ];
11768
+ 5 * MINUTE_MS;
11769
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
11770
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
11771
+ var rollingWindowObject = object({
11772
+ kind: literal("rolling"),
11773
+ span: _enum(ROLLING_SPANS),
11774
+ customMinutes: number().int().min(5).optional()
11775
+ }).check((payload) => {
11776
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
11777
+ code: "custom",
11778
+ message: "a custom range needs a duration in minutes",
11779
+ path: ["customMinutes"],
11780
+ input: payload.value
11781
+ });
11782
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
11783
+ code: "custom",
11784
+ message: "only a custom range carries a duration",
11785
+ path: ["customMinutes"],
11786
+ input: payload.value
11787
+ });
11788
+ });
11789
+ var RollingWindowSchema = rollingWindowObject;
11790
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
11791
+ kind: literal("calendar"),
11792
+ period: _enum(CALENDAR_PERIODS)
11793
+ })]);
11794
+ /**
11664
11795
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11665
11796
  * id. Split out so helper schemas can name it without a cycle through
11666
11797
  * `composition.ts` (D659).
@@ -11686,7 +11817,13 @@ var GROUP_TEMPLATE_IDS = [
11686
11817
  "occupancy",
11687
11818
  "binary",
11688
11819
  "switch",
11689
- "light"
11820
+ "light",
11821
+ "fan",
11822
+ "cover",
11823
+ "valve",
11824
+ "lock",
11825
+ "media-player",
11826
+ "climate"
11690
11827
  ];
11691
11828
  var NUMERIC_AGGREGATES = [
11692
11829
  "mean",
@@ -11716,14 +11853,73 @@ var HelperMemberSchema = object({
11716
11853
  cap: string().min(1),
11717
11854
  fieldPath: string().min(1)
11718
11855
  });
11719
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11720
- kind: literal("group"),
11721
- templateId: GroupTemplateIdSchema,
11722
- aggregate: AggregateReducerSchema,
11723
- members: array(HelperMemberSchema).min(1).max(64),
11724
- /** Actuator groups only. Readings leave this unset. */
11725
- active: ActiveCriterionSchema.optional()
11726
- })]);
11856
+ var HelperSpecSchema = discriminatedUnion("kind", [
11857
+ object({
11858
+ kind: literal("group"),
11859
+ templateId: GroupTemplateIdSchema,
11860
+ aggregate: AggregateReducerSchema,
11861
+ members: array(HelperMemberSchema).min(1).max(64),
11862
+ /** Actuator groups only. Readings leave this unset. */
11863
+ active: ActiveCriterionSchema.optional()
11864
+ }),
11865
+ object({
11866
+ kind: literal("appliance"),
11867
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
11868
+ appliance: _enum(APPLIANCE_KINDS),
11869
+ thresholdWatts: number().positive(),
11870
+ startDwellMs: number().int().nonnegative(),
11871
+ stopDwellMs: number().int().nonnegative(),
11872
+ member: HelperMemberSchema
11873
+ }),
11874
+ object({
11875
+ kind: literal("statistic"),
11876
+ stat: _enum(STATISTIC_STATS),
11877
+ window: StatisticWindowSchema,
11878
+ source: HelperMemberSchema
11879
+ }),
11880
+ object({
11881
+ kind: literal("integral"),
11882
+ reset: _enum(RESET_PERIODS),
11883
+ output: _enum(["energy", "meter"]),
11884
+ source: HelperMemberSchema
11885
+ }),
11886
+ object({
11887
+ kind: literal("threshold"),
11888
+ source: HelperMemberSchema,
11889
+ high: number(),
11890
+ low: number(),
11891
+ onForMs: number().int().nonnegative(),
11892
+ offForMs: number().int().nonnegative()
11893
+ }),
11894
+ object({
11895
+ kind: literal("arithmetic"),
11896
+ op: _enum([
11897
+ "sum",
11898
+ "difference",
11899
+ "ratio"
11900
+ ]),
11901
+ inputs: array(HelperMemberSchema).min(2).max(64)
11902
+ }),
11903
+ object({
11904
+ kind: literal("expression"),
11905
+ expr: string().min(1),
11906
+ inputs: array(object({
11907
+ name: string().min(1),
11908
+ member: HelperMemberSchema
11909
+ })).min(1).max(64),
11910
+ output: _enum(["number", "boolean"])
11911
+ }),
11912
+ object({
11913
+ kind: literal("derivative"),
11914
+ window: RollingWindowSchema,
11915
+ per: _enum([
11916
+ "second",
11917
+ "minute",
11918
+ "hour"
11919
+ ]),
11920
+ source: HelperMemberSchema
11921
+ })
11922
+ ]);
11727
11923
  /** A composed device's stableId is this prefix plus the block id. */
11728
11924
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11729
11925
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -11784,6 +11980,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11784
11980
  "all"
11785
11981
  ]),
11786
11982
  reads: array(CompositionFieldReadSchema).min(1).max(64),
11983
+ /**
11984
+ * Set when the reads are an enum state, not a yes/no: the listed states
11985
+ * count as active (a cover is `open`). A state outside the list is inactive;
11986
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
11987
+ */
11988
+ activeWhen: array(string().min(1)).min(1).optional(),
11787
11989
  /** `value` is the reading; the others are the member counts. */
11788
11990
  publish: _enum([
11789
11991
  "value",
@@ -11797,6 +11999,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11797
11999
  "omit",
11798
12000
  "offline"
11799
12001
  ])
12002
+ }),
12003
+ object({
12004
+ kind: literal("series"),
12005
+ read: CompositionFieldReadSchema,
12006
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12007
+ publish: _enum([
12008
+ "value",
12009
+ "included",
12010
+ "excluded",
12011
+ "total"
12012
+ ]),
12013
+ /**
12014
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12015
+ * device unseeded until history answers, so a restart cannot publish 0.
12016
+ */
12017
+ empty: _enum([
12018
+ "null",
12019
+ "omit",
12020
+ "offline"
12021
+ ])
11800
12022
  })
11801
12023
  ]);
11802
12024
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -15804,6 +16026,14 @@ var AutomationConditionLeafSchema = object({
15804
16026
  array(union([string(), number()]))
15805
16027
  ])
15806
16028
  });
16029
+ var AutomationConditionStationaryPresentSchema = object({
16030
+ kind: literal("stationary-present"),
16031
+ deviceId: number().int().nonnegative(),
16032
+ /** Class names; a macro class matches every raw class that maps to it. */
16033
+ classes: array(string().min(1)).min(1).optional(),
16034
+ /** The parked object must be in at least one of these zones. */
16035
+ zones: array(string().min(1)).min(1).optional()
16036
+ });
15807
16037
  /**
15808
16038
  * The expression leaf, declared as a plain object rather than an intersection
15809
16039
  * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
@@ -15837,7 +16067,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
15837
16067
  child: AutomationConditionSchema
15838
16068
  }),
15839
16069
  AutomationConditionLeafSchema,
15840
- AutomationConditionExpressionSchema
16070
+ AutomationConditionExpressionSchema,
16071
+ AutomationConditionStationaryPresentSchema
15841
16072
  ]));
15842
16073
  /**
15843
16074
  * What starts a run.
@@ -15878,7 +16109,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15878
16109
  kind: literal("schedule"),
15879
16110
  cron: string().min(1).max(120)
15880
16111
  }),
15881
- object({ kind: literal("manual") })
16112
+ object({ kind: literal("manual") }),
16113
+ object({
16114
+ kind: literal("detection"),
16115
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
16116
+ * matches every raw class that maps to it; any other name matches
16117
+ * exactly. */
16118
+ classes: array(string().min(1)).min(1),
16119
+ deviceIds: array(number().int().nonnegative()).min(1),
16120
+ /** The track must have visited at least one of these zones. */
16121
+ zones: array(string().min(1)).min(1).optional(),
16122
+ /** Best-so-far confidence the track must reach. */
16123
+ minScore: number().min(0).max(1).optional()
16124
+ }),
16125
+ object({
16126
+ kind: literal("stationary"),
16127
+ deviceIds: array(number().int().nonnegative()).min(1),
16128
+ /** Class names; a macro class matches like the detection trigger. */
16129
+ classes: array(string().min(1)).min(1).optional(),
16130
+ /** The parked object must be in at least one of these zones. */
16131
+ zones: array(string().min(1)).min(1).optional(),
16132
+ /** `appeared`: it became parked. `departed`: the registry retired it.
16133
+ * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
16134
+ phase: _enum(["appeared", "departed"])
16135
+ }),
16136
+ object({
16137
+ kind: literal("interval"),
16138
+ minutes: number().int().min(1).max(1440)
16139
+ })
15882
16140
  ]);
15883
16141
  /**
15884
16142
  * One action step.
@@ -15898,28 +16156,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15898
16156
  * is called. The runner still receives a number and is untouched; the
15899
16157
  * resolution is the recipe's job, not the runner's.
15900
16158
  */
16159
+ var AutomationWaitStepSchema = object({
16160
+ kind: literal("wait"),
16161
+ seconds: number().min(0).max(300)
16162
+ });
16163
+ var AutomationCapStepSchema = object({
16164
+ kind: literal("cap"),
16165
+ /**
16166
+ * One device, several same-family devices (locks / climate family / …),
16167
+ * or `{ $var }` for the device that triggered the run. An array is
16168
+ * fan-out at codegen — each id gets the same cap.method call.
16169
+ */
16170
+ deviceId: union([
16171
+ number().int(),
16172
+ array(number().int()).min(1),
16173
+ object({ $var: string().min(1) })
16174
+ ]),
16175
+ cap: string().min(1),
16176
+ method: string().min(1),
16177
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
16178
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
16179
+ * the expression leaf. */
16180
+ args: record(string(), unknown()).optional()
16181
+ });
16182
+ /**
16183
+ * The steps an internal Action may contain: cap and wait, and nothing that can
16184
+ * call back into an Action — so an Action can never reach itself (no cycles).
16185
+ */
16186
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
15901
16187
  var AutomationActionSchema = discriminatedUnion("kind", [
16188
+ AutomationWaitStepSchema,
16189
+ AutomationCapStepSchema,
15902
16190
  object({
15903
- kind: literal("wait"),
15904
- seconds: number().min(0).max(300)
15905
- }),
15906
- object({
15907
- kind: literal("cap"),
15908
- /**
15909
- * One device, several same-family devices (locks / climate family / …),
15910
- * or `{ $var }` for the device that triggered the run. An array is
15911
- * fan-out at codegen — each id gets the same cap.method call.
15912
- */
15913
- deviceId: union([
15914
- number().int(),
15915
- array(number().int()).min(1),
15916
- object({ $var: string().min(1) })
15917
- ]),
15918
- cap: string().min(1),
15919
- method: string().min(1),
15920
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
15921
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
15922
- args: record(string(), unknown()).optional()
16191
+ kind: literal("action"),
16192
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
16193
+ actionId: string().min(1),
16194
+ /** Values for the action's declared variables; the others take their default. */
16195
+ variables: record(string(), union([
16196
+ string(),
16197
+ number(),
16198
+ boolean()
16199
+ ])).optional()
15923
16200
  }),
15924
16201
  object({
15925
16202
  kind: literal("code"),
@@ -15928,10 +16205,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15928
16205
  code: string().min(1).max(2e4)
15929
16206
  })
15930
16207
  ]);
16208
+ /**
16209
+ * How a trigger that arrives while the automation is busy (or cooling down) is
16210
+ * handled. One of the automation's execution modes, never a property of its
16211
+ * actions. Absent means `single`.
16212
+ *
16213
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
16214
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
16215
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
16216
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
16217
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
16218
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
16219
+ * wait, never the single/queued limits.
16220
+ */
16221
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
16222
+ object({ mode: literal("single") }),
16223
+ object({ mode: literal("restart") }),
16224
+ object({
16225
+ mode: literal("queued"),
16226
+ max: number().int().min(1).max(10)
16227
+ }),
16228
+ object({
16229
+ mode: literal("parallel"),
16230
+ max: number().int().min(1).max(10)
16231
+ }),
16232
+ object({
16233
+ mode: literal("cooldown"),
16234
+ minutes: number().int().min(1).max(1440)
16235
+ })
16236
+ ]);
15931
16237
  var AutomationRecipeSchema = object({
15932
16238
  triggers: array(AutomationTriggerSchema),
15933
16239
  conditions: AutomationConditionSchema.optional(),
15934
- actions: array(AutomationActionSchema)
16240
+ actions: array(AutomationActionSchema),
16241
+ runMode: AutomationRunModeSchema.optional(),
16242
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
16243
+ keepTraces: number().int().min(1).max(100).optional()
16244
+ });
16245
+ new Set([
16246
+ {
16247
+ id: "person",
16248
+ name: "Person"
16249
+ },
16250
+ {
16251
+ id: "vehicle",
16252
+ name: "Vehicle"
16253
+ },
16254
+ {
16255
+ id: "animal",
16256
+ name: "Animal"
16257
+ },
16258
+ {
16259
+ id: "package",
16260
+ name: "Package"
16261
+ }
16262
+ ].map((l) => l.id));
16263
+ var COCO_TO_MACRO = {
16264
+ mapping: {
16265
+ person: "person",
16266
+ bicycle: "vehicle",
16267
+ car: "vehicle",
16268
+ motorcycle: "vehicle",
16269
+ airplane: "vehicle",
16270
+ bus: "vehicle",
16271
+ train: "vehicle",
16272
+ truck: "vehicle",
16273
+ boat: "vehicle",
16274
+ bird: "animal",
16275
+ cat: "animal",
16276
+ dog: "animal",
16277
+ horse: "animal",
16278
+ sheep: "animal",
16279
+ cow: "animal",
16280
+ elephant: "animal",
16281
+ bear: "animal",
16282
+ zebra: "animal",
16283
+ giraffe: "animal",
16284
+ suitcase: "package",
16285
+ backpack: "package",
16286
+ handbag: "package"
16287
+ },
16288
+ preserveOriginal: false
16289
+ };
16290
+ new Set(Object.values(COCO_TO_MACRO.mapping));
16291
+ /**
16292
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
16293
+ * so the editor can paint every block green or red with its reason.
16294
+ *
16295
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
16296
+ * every condition node (containers and leaves, including the ones a
16297
+ * short-circuit never reached), the run-mode verdict and every action step —
16298
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
16299
+ * for an Action run). Skipped
16300
+ * evaluations are traced too, and identical consecutive skips collapse into a
16301
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
16302
+ *
16303
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
16304
+ * vocabulary so an Action run records the same step shape.
16305
+ */
16306
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
16307
+ var TraceValueSchema = union([
16308
+ string(),
16309
+ number(),
16310
+ boolean(),
16311
+ _null(),
16312
+ array(union([string(), number()]))
16313
+ ]);
16314
+ var AutomationRunOutcomeSchema = _enum([
16315
+ "ran",
16316
+ "skipped-trigger",
16317
+ "skipped-conditions",
16318
+ "skipped-cooldown",
16319
+ "skipped-overlap",
16320
+ "failed",
16321
+ "aborted"
16322
+ ]);
16323
+ /** `not-evaluated` = a short-circuit never reached the node. */
16324
+ var TraceNodeOutcomeSchema = _enum([
16325
+ "pass",
16326
+ "fail",
16327
+ "not-evaluated"
16328
+ ]);
16329
+ var AutomationTriggerTraceSchema = object({
16330
+ kind: string().min(1),
16331
+ outcome: _enum(["pass", "fail"]),
16332
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
16333
+ reason: string(),
16334
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
16335
+ vars: record(string(), TraceValueSchema)
16336
+ });
16337
+ var AutomationConditionTraceNodeSchema = object({
16338
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
16339
+ path: string().min(1),
16340
+ kind: _enum([
16341
+ "all",
16342
+ "any",
16343
+ "not",
16344
+ "condition",
16345
+ "expression",
16346
+ "stationary-present"
16347
+ ]),
16348
+ outcome: TraceNodeOutcomeSchema,
16349
+ reason: string(),
16350
+ deviceId: number().int().optional(),
16351
+ cap: string().optional(),
16352
+ fieldPath: string().optional(),
16353
+ operator: string().optional(),
16354
+ expected: TraceValueSchema.optional(),
16355
+ /** What the mirror held; `null` when it held nothing (unknown). */
16356
+ observed: TraceValueSchema.optional()
16357
+ });
16358
+ var AutomationRunVerdictSchema = object({
16359
+ mode: AutomationRunModeSchema,
16360
+ decision: _enum([
16361
+ "run",
16362
+ "queued",
16363
+ "restart",
16364
+ "drop",
16365
+ "cooldown",
16366
+ "bypass"
16367
+ ]),
16368
+ reason: string()
16369
+ });
16370
+ /** One executed (or not-run) step — shared by automations and Action runs. */
16371
+ var AutomationStepTraceSchema = object({
16372
+ /** `actions.<i>` */
16373
+ path: string().min(1),
16374
+ kind: string().min(1),
16375
+ cap: string().optional(),
16376
+ method: string().optional(),
16377
+ deviceId: number().int().optional(),
16378
+ startedAt: number().optional(),
16379
+ endedAt: number().optional(),
16380
+ outcome: _enum([
16381
+ "ok",
16382
+ "error",
16383
+ "not-run"
16384
+ ]),
16385
+ message: string().optional()
16386
+ });
16387
+ var AutomationRunTraceSchema = object({
16388
+ runId: string().min(1),
16389
+ startedAt: number(),
16390
+ endedAt: number(),
16391
+ trigger: AutomationTriggerTraceSchema,
16392
+ conditions: array(AutomationConditionTraceNodeSchema),
16393
+ verdict: AutomationRunVerdictSchema.optional(),
16394
+ steps: array(AutomationStepTraceSchema),
16395
+ outcome: AutomationRunOutcomeSchema,
16396
+ /** How many identical consecutive skips this trace stands for (>= 1). */
16397
+ repeat: number().int().min(1),
16398
+ /** When the latest of those repeats happened. */
16399
+ lastAt: number()
16400
+ });
16401
+ /**
16402
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
16403
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
16404
+ */
16405
+ var AutomationStateDocSchema = object({
16406
+ version: literal(1),
16407
+ lastRunStartedAt: number().nullable(),
16408
+ traces: array(AutomationRunTraceSchema)
16409
+ });
16410
+ /**
16411
+ * Actions — named, runnable things an operator (or an automation) can start,
16412
+ * declared as LIST ENTRIES, never as devices.
16413
+ *
16414
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
16415
+ * hub's `actions` record kind) or declared by an addon through the
16416
+ * `action-source` collection (a Home Assistant script, a scene …). The one
16417
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
16418
+ * catalog of its own (D3) — every `list` asks the providers live.
16419
+ *
16420
+ * This file is the data half: the entry schema, the variables an entry may
16421
+ * declare, and the pure resolution of the values a run was given. It holds no
16422
+ * runtime.
16423
+ */
16424
+ /** What a variable's value is. `select` takes one of `options`. */
16425
+ var ActionVariableTypeSchema = _enum([
16426
+ "string",
16427
+ "number",
16428
+ "boolean",
16429
+ "select"
16430
+ ]);
16431
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
16432
+ var ActionVariableValueSchema = union([
16433
+ string(),
16434
+ number(),
16435
+ boolean()
16436
+ ]);
16437
+ /**
16438
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
16439
+ * value) or `{{vars.name}}` (inside a string) in their args.
16440
+ */
16441
+ var ActionVariableSchema = object({
16442
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
16443
+ label: string().min(1).max(120).optional(),
16444
+ type: ActionVariableTypeSchema,
16445
+ required: boolean().optional(),
16446
+ default: ActionVariableValueSchema.optional(),
16447
+ /** The allowed values of a `select`. */
16448
+ options: array(string().min(1)).min(1).optional()
16449
+ }).superRefine((v, ctx) => {
16450
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
16451
+ code: "custom",
16452
+ message: "a select variable needs options",
16453
+ path: ["options"]
16454
+ });
16455
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
16456
+ code: "custom",
16457
+ message: variableValueError(v, v.default) ?? "invalid default",
16458
+ path: ["default"]
16459
+ });
16460
+ });
16461
+ /** Why `value` is not a legal value for the variable, or null when it is. */
16462
+ function variableValueError(variable, value) {
16463
+ switch (variable.type) {
16464
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
16465
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
16466
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
16467
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
16468
+ }
16469
+ }
16470
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
16471
+ function isInternalActionId(actionId) {
16472
+ return actionId.startsWith("internal:") && actionId.length > 9;
16473
+ }
16474
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
16475
+ function addonIdOfActionId(actionId) {
16476
+ if (isInternalActionId(actionId)) return null;
16477
+ const at = actionId.indexOf(":");
16478
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
16479
+ }
16480
+ var ActionStatusSchema = object({
16481
+ isRunning: boolean(),
16482
+ /** Ms epoch the latest run STARTED; null when it never ran. */
16483
+ lastRunAt: number().nullable(),
16484
+ /** The latest run's failure; null on success or when it never ran. */
16485
+ lastError: string().nullable()
16486
+ });
16487
+ var ActionEntrySchema = object({
16488
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
16489
+ id: string().min(3),
16490
+ name: string().min(1).max(120),
16491
+ /** The addon that declared it; `core-blocks` for an internal one. */
16492
+ addonId: string().min(1),
16493
+ source: ActionSourceKindSchema,
16494
+ /** An addon-declared entry is edited where it was declared. */
16495
+ readOnly: boolean(),
16496
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
16497
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
16498
+ variables: array(ActionVariableSchema).max(20).optional(),
16499
+ status: ActionStatusSchema
16500
+ }).superRefine((entry, ctx) => {
16501
+ if (entry.source === "internal") {
16502
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
16503
+ code: "custom",
16504
+ message: "an internal action id is internal:<uuid>",
16505
+ path: ["id"]
16506
+ });
16507
+ return;
16508
+ }
16509
+ if (entry.steps !== void 0) ctx.addIssue({
16510
+ code: "custom",
16511
+ message: "only an internal action carries steps",
16512
+ path: ["steps"]
16513
+ });
16514
+ if (!entry.readOnly) ctx.addIssue({
16515
+ code: "custom",
16516
+ message: "an addon action is read-only here",
16517
+ path: ["readOnly"]
16518
+ });
16519
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
16520
+ code: "custom",
16521
+ message: "an addon action id is <addonId>:<localId>",
16522
+ path: ["id"]
16523
+ });
16524
+ });
16525
+ /** What an operator writes for an internal action. */
16526
+ var ActionInputSchema = object({
16527
+ name: string().min(1).max(120),
16528
+ steps: array(ActionStepSchema).min(1).max(50),
16529
+ variables: array(ActionVariableSchema).max(20).optional()
16530
+ });
16531
+ /** Who started a run. Absent = the operator pressed it. */
16532
+ var ActionRunOriginSchema = object({
16533
+ /** The automation block whose step started it. */
16534
+ blockId: string().min(1).optional() });
16535
+ /** `started` = the caller did not ask to wait. */
16536
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
16537
+ var ActionRunResultSchema = object({
16538
+ runId: string().min(1),
16539
+ outcome: ActionRunOutcomeSchema,
16540
+ /** Why it did not run, or what failed. */
16541
+ message: string().optional()
16542
+ });
16543
+ _enum([
16544
+ "not-found",
16545
+ "read-only",
16546
+ "invalid-variables",
16547
+ "invalid-steps",
16548
+ "not-linked",
16549
+ "busy"
16550
+ ]);
16551
+ object({
16552
+ actionId: string(),
16553
+ runId: string(),
16554
+ isRunning: boolean(),
16555
+ /** Set when the run ended. */
16556
+ outcome: AutomationRunOutcomeSchema.optional(),
16557
+ lastRunAt: number().nullable(),
16558
+ lastError: string().nullable()
15935
16559
  });
15936
16560
  /**
15937
16561
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -17526,7 +18150,22 @@ var BrokerProviderInfoSchema = object({
17526
18150
  */
17527
18151
  interactiveConnectPath: string().min(1).optional(),
17528
18152
  /** Relative hub path to disconnect the linked account (same popup pattern). */
17529
- interactiveDisconnectPath: string().min(1).optional()
18153
+ interactiveDisconnectPath: string().min(1).optional(),
18154
+ /**
18155
+ * The kind signs in through a server-held interactive session instead of
18156
+ * (or before) typed credentials. `basePath` is a hub-relative route set
18157
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
18158
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
18159
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
18160
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
18161
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
18162
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
18163
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
18164
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
18165
+ * offers it as the default path; the fields the session fills are
18166
+ * marked `filledBySignIn` in the kind's settings schema.
18167
+ */
18168
+ signIn: object({ basePath: string().min(1) }).optional()
17530
18169
  }))
17531
18170
  });
17532
18171
  var ListInputSchema = object({
@@ -18603,7 +19242,7 @@ var CompositionEventPlanSchema = object({
18603
19242
  stateField: string(),
18604
19243
  fieldKey: string()
18605
19244
  });
18606
- /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
19245
+ /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
18607
19246
  var CompositionProblemSchema = object({
18608
19247
  code: _enum([
18609
19248
  "reserved-variant",
@@ -18631,7 +19270,8 @@ var CompositionProblemSchema = object({
18631
19270
  "native-capability-replace",
18632
19271
  "mixed-quantity",
18633
19272
  "helper-mismatch",
18634
- "member-constraint"
19273
+ "member-constraint",
19274
+ "retention"
18635
19275
  ]),
18636
19276
  path: string(),
18637
19277
  message: string()
@@ -18865,13 +19505,17 @@ Object.freeze({
18865
19505
  latchedAt: {
18866
19506
  kind: "fixed",
18867
19507
  result: ["number", "null"]
19508
+ },
19509
+ schmitt: {
19510
+ kind: "fixed",
19511
+ result: ["boolean", "null"]
18868
19512
  }
18869
19513
  });
18870
19514
  /**
18871
19515
  * One catalog row per base reading a group helper can build (D944 §3).
18872
19516
  * Data, not a form default written into the block.
18873
19517
  */
18874
- var NOW = {
19518
+ var NOW$2 = {
18875
19519
  fieldPath: "lastFetchedAt",
18876
19520
  expr: "now"
18877
19521
  };
@@ -18885,6 +19529,8 @@ function numeric(input) {
18885
19529
  family: "numeric",
18886
19530
  deviceType: DeviceType.Sensor,
18887
19531
  memberDeviceType: DeviceType.Sensor,
19532
+ memberDeviceTypes: [DeviceType.Sensor],
19533
+ activeWhen: void 0,
18888
19534
  commandCaps: [],
18889
19535
  baseUnit: baseUnitOf(input.quantity),
18890
19536
  aggregates: NUMERIC_AGGREGATES,
@@ -18900,6 +19546,8 @@ function binary(input) {
18900
19546
  family: "binary",
18901
19547
  deviceType: DeviceType.Sensor,
18902
19548
  memberDeviceType: DeviceType.Sensor,
19549
+ memberDeviceTypes: [DeviceType.Sensor],
19550
+ activeWhen: void 0,
18903
19551
  commandCaps: [],
18904
19552
  quantity: "none",
18905
19553
  baseUnit: void 0,
@@ -18913,73 +19561,73 @@ var GROUP_TEMPLATES = [
18913
19561
  summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18914
19562
  role: DeviceRole.TemperatureSensor,
18915
19563
  outputCap: "temperature-sensor",
18916
- outputField: "celsius",
19564
+ outputField: "temperature",
18917
19565
  quantity: "temperature",
18918
19566
  defaultAggregate: "mean",
18919
19567
  empty: "null",
18920
- companions: [NOW]
19568
+ companions: [NOW$2]
18921
19569
  }),
18922
19570
  numeric({
18923
19571
  id: "humidity",
18924
19572
  summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18925
19573
  role: DeviceRole.HumiditySensor,
18926
19574
  outputCap: "humidity-sensor",
18927
- outputField: "percent",
19575
+ outputField: "humidity",
18928
19576
  quantity: "humidity",
18929
19577
  defaultAggregate: "mean",
18930
19578
  empty: "null",
18931
- companions: [NOW]
19579
+ companions: [NOW$2]
18932
19580
  }),
18933
19581
  numeric({
18934
19582
  id: "pressure",
18935
19583
  summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18936
19584
  role: DeviceRole.PressureSensor,
18937
19585
  outputCap: "pressure-sensor",
18938
- outputField: "hpa",
19586
+ outputField: "pressure",
18939
19587
  quantity: "pressure",
18940
19588
  defaultAggregate: "mean",
18941
19589
  empty: "null",
18942
- companions: [NOW]
19590
+ companions: [NOW$2]
18943
19591
  }),
18944
19592
  numeric({
18945
19593
  id: "illuminance",
18946
19594
  summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18947
19595
  role: DeviceRole.AmbientLightSensor,
18948
19596
  outputCap: "ambient-light-sensor",
18949
- outputField: "lux",
19597
+ outputField: "illuminance",
18950
19598
  quantity: "illuminance",
18951
19599
  defaultAggregate: "mean",
18952
19600
  empty: "null",
18953
- companions: [NOW]
19601
+ companions: [NOW$2]
18954
19602
  }),
18955
19603
  numeric({
18956
19604
  id: "power",
18957
19605
  summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18958
19606
  role: DeviceRole.PowerSensor,
18959
19607
  outputCap: "power-meter",
18960
- outputField: "watts",
19608
+ outputField: "power",
18961
19609
  quantity: "power",
18962
19610
  defaultAggregate: "sum",
18963
19611
  empty: "omit",
18964
- companions: [NOW]
19612
+ companions: [NOW$2]
18965
19613
  }),
18966
19614
  numeric({
18967
19615
  id: "energy",
18968
19616
  summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18969
19617
  role: DeviceRole.EnergySensor,
18970
19618
  outputCap: "power-meter",
18971
- outputField: "kwhTotal",
19619
+ outputField: "energy",
18972
19620
  quantity: "energy",
18973
19621
  defaultAggregate: "sum",
18974
19622
  empty: "omit",
18975
- companions: [NOW]
19623
+ companions: [NOW$2]
18976
19624
  }),
18977
19625
  numeric({
18978
19626
  id: "battery",
18979
19627
  summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18980
19628
  role: DeviceRole.BatterySensor,
18981
19629
  outputCap: "battery",
18982
- outputField: "percentage",
19630
+ outputField: "level",
18983
19631
  quantity: "percentage",
18984
19632
  defaultAggregate: "mean",
18985
19633
  empty: "null",
@@ -19007,7 +19655,7 @@ var GROUP_TEMPLATES = [
19007
19655
  quantity: "device",
19008
19656
  defaultAggregate: "mean",
19009
19657
  empty: "null",
19010
- companions: [NOW]
19658
+ companions: [NOW$2]
19011
19659
  }),
19012
19660
  binary({
19013
19661
  id: "motion",
@@ -19101,6 +19749,11 @@ var GROUP_TEMPLATES = [
19101
19749
  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.",
19102
19750
  deviceType: DeviceType.Switch,
19103
19751
  outputCap: "switch",
19752
+ outputField: "on",
19753
+ member: {
19754
+ cap: "switch",
19755
+ fieldPath: "on"
19756
+ },
19104
19757
  commandCaps: ["switch"]
19105
19758
  }),
19106
19759
  actuator({
@@ -19108,34 +19761,136 @@ var GROUP_TEMPLATES = [
19108
19761
  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.",
19109
19762
  deviceType: DeviceType.Light,
19110
19763
  outputCap: "switch",
19764
+ outputField: "on",
19765
+ member: {
19766
+ cap: "switch",
19767
+ fieldPath: "on"
19768
+ },
19111
19769
  commandCaps: [
19112
19770
  "switch",
19113
19771
  "brightness",
19114
19772
  "color"
19115
19773
  ]
19774
+ }),
19775
+ actuator({
19776
+ id: "fan",
19777
+ 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.",
19778
+ deviceType: DeviceType.Fan,
19779
+ outputCap: "switch",
19780
+ outputField: "on",
19781
+ member: {
19782
+ cap: "switch",
19783
+ fieldPath: "on"
19784
+ },
19785
+ commandCaps: ["switch", "fan-control"]
19786
+ }),
19787
+ actuator({
19788
+ id: "cover",
19789
+ 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.",
19790
+ deviceType: DeviceType.Cover,
19791
+ outputCap: "group-active",
19792
+ outputField: "active",
19793
+ member: {
19794
+ cap: "cover",
19795
+ fieldPath: "state"
19796
+ },
19797
+ activeWhen: [
19798
+ "open",
19799
+ "opening",
19800
+ "stopped"
19801
+ ],
19802
+ commandCaps: ["cover"]
19803
+ }),
19804
+ actuator({
19805
+ id: "valve",
19806
+ 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.",
19807
+ deviceType: DeviceType.Valve,
19808
+ outputCap: "group-active",
19809
+ outputField: "active",
19810
+ member: {
19811
+ cap: "valve",
19812
+ fieldPath: "state"
19813
+ },
19814
+ activeWhen: [
19815
+ "open",
19816
+ "opening",
19817
+ "stopped"
19818
+ ],
19819
+ commandCaps: ["valve"]
19820
+ }),
19821
+ actuator({
19822
+ id: "lock",
19823
+ 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.",
19824
+ deviceType: DeviceType.Lock,
19825
+ outputCap: "group-active",
19826
+ outputField: "active",
19827
+ member: {
19828
+ cap: "lock-control",
19829
+ fieldPath: "state"
19830
+ },
19831
+ activeWhen: ["locked"],
19832
+ commandCaps: ["lock-control"]
19833
+ }),
19834
+ actuator({
19835
+ id: "media-player",
19836
+ 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.",
19837
+ deviceType: DeviceType.MediaPlayer,
19838
+ outputCap: "group-active",
19839
+ outputField: "active",
19840
+ member: {
19841
+ cap: "media-player",
19842
+ fieldPath: "state"
19843
+ },
19844
+ activeWhen: ["playing", "buffering"],
19845
+ commandCaps: ["media-player"]
19846
+ }),
19847
+ actuator({
19848
+ id: "climate",
19849
+ 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.",
19850
+ deviceType: DeviceType.Thermostat,
19851
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
19852
+ outputCap: "group-active",
19853
+ outputField: "active",
19854
+ member: {
19855
+ cap: "climate-control",
19856
+ fieldPath: "mode"
19857
+ },
19858
+ activeWhen: [
19859
+ "heat",
19860
+ "cool",
19861
+ "auto",
19862
+ "heat_cool",
19863
+ "fan_only",
19864
+ "dry"
19865
+ ],
19866
+ commandCaps: ["climate-control"]
19116
19867
  })
19117
19868
  ];
19118
19869
  function actuator(input) {
19119
19870
  return {
19120
- ...input,
19871
+ id: input.id,
19872
+ summary: input.summary,
19873
+ deviceType: input.deviceType,
19874
+ outputCap: input.outputCap,
19875
+ outputField: input.outputField,
19876
+ commandCaps: input.commandCaps,
19121
19877
  family: "actuator",
19122
19878
  role: void 0,
19123
19879
  memberDeviceType: input.deviceType,
19124
- outputField: "on",
19880
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
19881
+ activeWhen: input.activeWhen,
19125
19882
  quantity: "none",
19126
19883
  baseUnit: void 0,
19127
19884
  aggregates: BINARY_AGGREGATES,
19128
19885
  defaultAggregate: "any",
19129
19886
  empty: "offline",
19130
- accept: [{
19131
- cap: "switch",
19132
- fieldPath: "on"
19133
- }],
19887
+ accept: [input.member],
19134
19888
  companions: [CHANGED]
19135
19889
  };
19136
19890
  }
19137
19891
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
19138
19892
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
19893
+ DeviceType.Camera;
19139
19894
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19140
19895
  /**
19141
19896
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -19450,7 +20205,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
19450
20205
  kind: "mutation",
19451
20206
  auth: "admin",
19452
20207
  access: "view"
19453
- });
20208
+ }), method(object({
20209
+ blockId: string().min(1),
20210
+ trace: AutomationRunTraceSchema.optional(),
20211
+ runStartedAt: number().optional(),
20212
+ keepTraces: number().int().min(1).max(100).optional()
20213
+ }), _void(), {
20214
+ kind: "mutation",
20215
+ auth: "admin"
20216
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
20217
+ traces: array(AutomationRunTraceSchema),
20218
+ lastError: string().nullable()
20219
+ }), { auth: "admin" });
19454
20220
  /**
19455
20221
  * `custom-model-registry` — collection cap exposing operator-registered
19456
20222
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -44495,7 +45261,7 @@ var NetworkAccessStatusSchema = object({
44495
45261
  });
44496
45262
  /**
44497
45263
  * Optional, richer endpoint shape returned by providers that expose
44498
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
45264
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44499
45265
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44500
45266
  * the originating provider config (mode + sourcePort) so the
44501
45267
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -44888,51 +45654,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
44888
45654
  kind: "mutation",
44889
45655
  auth: "admin"
44890
45656
  });
44891
- new Set([
44892
- {
44893
- id: "person",
44894
- name: "Person"
44895
- },
44896
- {
44897
- id: "vehicle",
44898
- name: "Vehicle"
44899
- },
44900
- {
44901
- id: "animal",
44902
- name: "Animal"
44903
- },
44904
- {
44905
- id: "package",
44906
- name: "Package"
44907
- }
44908
- ].map((l) => l.id));
44909
- var COCO_TO_MACRO = {
44910
- mapping: {
44911
- person: "person",
44912
- bicycle: "vehicle",
44913
- car: "vehicle",
44914
- motorcycle: "vehicle",
44915
- airplane: "vehicle",
44916
- bus: "vehicle",
44917
- train: "vehicle",
44918
- truck: "vehicle",
44919
- boat: "vehicle",
44920
- bird: "animal",
44921
- cat: "animal",
44922
- dog: "animal",
44923
- horse: "animal",
44924
- sheep: "animal",
44925
- cow: "animal",
44926
- elephant: "animal",
44927
- bear: "animal",
44928
- zebra: "animal",
44929
- giraffe: "animal",
44930
- suitcase: "package",
44931
- backpack: "package",
44932
- handbag: "package"
44933
- },
44934
- preserveOriginal: false
44935
- };
44936
45657
  var AUDIO_MACRO_LABELS = [
44937
45658
  {
44938
45659
  id: "speech",
@@ -47182,7 +47903,20 @@ var NcRuleInputSchema = object({
47182
47903
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47183
47904
  * path does not run Zod defaults.
47184
47905
  */
47185
- groupingSec: number().int().min(5).max(60).optional()
47906
+ groupingSec: number().int().min(5).max(60).optional(),
47907
+ /**
47908
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47909
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47910
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47911
+ * same mark the Library writes, and records whose notification it was and
47912
+ * when. A channel with no buttons gets the same two choices as links.
47913
+ *
47914
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47915
+ * cap path): absent and `false` both mean off, which is what every rule
47916
+ * authored before this field meant. An editor turns it off by writing
47917
+ * `false`, so it needs no `clear` key.
47918
+ */
47919
+ askVerdict: boolean().optional()
47186
47920
  });
47187
47921
  /**
47188
47922
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -58008,9 +58742,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
58008
58742
  */
58009
58743
  var AirQualitySensorStatusSchema = object({
58010
58744
  /** Carbon dioxide concentration in ppm. */
58011
- co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
58745
+ co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
58012
58746
  /** Total volatile organic compounds in ppb. */
58013
- vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
58747
+ voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
58014
58748
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
58015
58749
  pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
58016
58750
  /** Particulate matter ≤ 10 μm in µg/m³. */
@@ -58043,12 +58777,12 @@ var airQualitySensorCapability = {
58043
58777
  * History (D850): recorded series.
58044
58778
  */
58045
58779
  history: { fields: {
58046
- co2Ppm: {
58780
+ co2: {
58047
58781
  kind: "gauge",
58048
58782
  unit: "ppm",
58049
58783
  deadband: 5
58050
58784
  },
58051
- vocPpb: {
58785
+ voc: {
58052
58786
  kind: "gauge",
58053
58787
  unit: "ppb",
58054
58788
  deadband: 5
@@ -58079,7 +58813,7 @@ var airQualitySensorCapability = {
58079
58813
  */
58080
58814
  var AmbientLightSensorStatusSchema = object({
58081
58815
  /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
58082
- lux: number().min(0).nullable().meta(readingMeta("illuminance", {
58816
+ illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
58083
58817
  precision: 0,
58084
58818
  label: "Illuminance"
58085
58819
  })),
@@ -58108,7 +58842,7 @@ var ambientLightSensorCapability = {
58108
58842
  /**
58109
58843
  * History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
58110
58844
  */
58111
- history: { fields: { lux: {
58845
+ history: { fields: { illuminance: {
58112
58846
  kind: "gauge",
58113
58847
  unit: "lx",
58114
58848
  deadbandPct: 10,
@@ -58247,6 +58981,84 @@ var audioMetricsCapability = {
58247
58981
  */
58248
58982
  history: "none"
58249
58983
  };
58984
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58985
+ actionId: string().min(1),
58986
+ variables: record(string(), ActionVariableValueSchema).optional()
58987
+ }), _void(), {
58988
+ kind: "mutation",
58989
+ auth: "admin"
58990
+ }), method(object({ actionId: string().min(1) }), _void(), {
58991
+ kind: "mutation",
58992
+ auth: "admin"
58993
+ });
58994
+ /**
58995
+ * actions — the ONE surface for runnable actions, for the UI and for
58996
+ * automations. Provided by the core-blocks builtin on the hub.
58997
+ *
58998
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
58999
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
59000
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
59001
+ * the run status from the hub's `runs` ledger; there is no catalog here to
59002
+ * drift (D3).
59003
+ *
59004
+ * **Permissions follow the camera, as linked devices do.** An action is linked
59005
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
59006
+ * non-admin may `get` / `run` an action only through a camera they may see that
59007
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
59008
+ * gate sees it) and the provider refuses an action that camera does not link.
59009
+ * An admin, and an in-process caller (an automation block), may run any action.
59010
+ * Every refusal is typed (`[not-linked] …`) and logged.
59011
+ *
59012
+ * A second `run` of an action that is already running is REFUSED — traced and
59013
+ * logged — never queued.
59014
+ */
59015
+ var ActionIdInput = object({ actionId: string().min(1) });
59016
+ var ActionLinksSchema = object({
59017
+ deviceId: number().int().nonnegative(),
59018
+ actionIds: array(string().min(1)).max(100)
59019
+ });
59020
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
59021
+ actionId: string().min(1),
59022
+ deviceId: number().int().nonnegative().optional()
59023
+ }), object({ action: ActionEntrySchema.nullable() }), {
59024
+ role: "viewer",
59025
+ auth: "protected",
59026
+ kind: "query",
59027
+ caller: "required"
59028
+ }), method(object({
59029
+ actionId: string().min(1),
59030
+ variables: record(string(), ActionVariableValueSchema).optional(),
59031
+ wait: boolean().optional(),
59032
+ origin: ActionRunOriginSchema.optional(),
59033
+ deviceId: number().int().nonnegative().optional()
59034
+ }), ActionRunResultSchema, {
59035
+ role: "viewer",
59036
+ auth: "protected",
59037
+ kind: "mutation",
59038
+ caller: "required"
59039
+ }), method(ActionIdInput, _void(), {
59040
+ kind: "mutation",
59041
+ auth: "admin"
59042
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
59043
+ kind: "mutation",
59044
+ auth: "admin"
59045
+ }), method(object({
59046
+ actionId: string().min(1),
59047
+ action: ActionInputSchema.partial()
59048
+ }), object({ action: ActionEntrySchema }), {
59049
+ kind: "mutation",
59050
+ auth: "admin"
59051
+ }), method(ActionIdInput, _void(), {
59052
+ kind: "mutation",
59053
+ auth: "admin"
59054
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
59055
+ role: "viewer",
59056
+ auth: "protected",
59057
+ kind: "query"
59058
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
59059
+ kind: "mutation",
59060
+ auth: "admin"
59061
+ });
58250
59062
  /**
58251
59063
  * Automation-control cap. Models HA `automation.*` entities on
58252
59064
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -58343,7 +59155,7 @@ var BatteryStatusSchema = object({
58343
59155
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
58344
59156
  * declines to band a non-finite reading, which is the correct shape.
58345
59157
  */
58346
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
59158
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
58347
59159
  /**
58348
59160
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
58349
59161
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -58353,7 +59165,7 @@ var BatteryStatusSchema = object({
58353
59165
  * `'unknown'` is the absence of an answer: the provider has not read the
58354
59166
  * power fields yet, or the firmware reported neither. Before it existed,
58355
59167
  * two unreadable fields produced `'none'`, and a camera nobody had reached
58356
- * was drawn "on battery alone" — the same D315 defect `percentage` closed
59168
+ * was drawn "on battery alone" — the same D315 defect `level` closed
58357
59169
  * by going nullable. Consumers SKIP it (no charger state published, no
58358
59170
  * glyph, no HomeKit ChargingState) rather than coerce it either way.
58359
59171
  */
@@ -58377,7 +59189,7 @@ var BatteryStatusSchema = object({
58377
59189
  * that only report charge. A second reading on this cap, not a second
58378
59190
  * `numeric-sensor` on the same device.
58379
59191
  */
58380
- volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
59192
+ voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
58381
59193
  /**
58382
59194
  * Ms epoch of the last time the device PROVED it was reachable — a
58383
59195
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -58411,7 +59223,7 @@ var BatteryStatusSchema = object({
58411
59223
  /**
58412
59224
  * True when the source is a BINARY low-battery indicator (HA
58413
59225
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
58414
- * charge level — `percentage` is then a coarse stand-in (100 = normal,
59226
+ * charge level — `level` is then a coarse stand-in (100 = normal,
58415
59227
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
58416
59228
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
58417
59229
  */
@@ -58424,21 +59236,22 @@ var BATTERY_DEVICE_TYPES = [
58424
59236
  DeviceType.Switch,
58425
59237
  DeviceType.PetFeeder,
58426
59238
  DeviceType.Container,
58427
- DeviceType.Thermostat
59239
+ DeviceType.Thermostat,
59240
+ DeviceType.EventEmitter
58428
59241
  ];
58429
59242
  /** Generic Customize target for the percentage reading (D936). */
58430
59243
  var batteryCustomize = {
58431
59244
  version: 1,
58432
59245
  targets: [{
58433
- id: "percentage",
59246
+ id: "level",
58434
59247
  cardinality: "scalar",
58435
59248
  vocabulary: {
58436
59249
  cap: "battery",
58437
- stateField: "percentage"
59250
+ stateField: "level"
58438
59251
  },
58439
59252
  deviceTypes: BATTERY_DEVICE_TYPES,
58440
59253
  apply: "add",
58441
- path: "percentage",
59254
+ path: "level",
58442
59255
  sources: [{
58443
59256
  kind: "field",
58444
59257
  valueKinds: ["number", "string"],
@@ -58532,7 +59345,7 @@ onStatusChanged: { data: object({
58532
59345
  schema: BatteryStatusSchema,
58533
59346
  kind: "push",
58534
59347
  empty: {
58535
- percentage: null,
59348
+ level: null,
58536
59349
  charging: "unknown",
58537
59350
  sleeping: false,
58538
59351
  lastUpdated: 0
@@ -58559,11 +59372,11 @@ onStatusChanged: { data: object({
58559
59372
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
58560
59373
  */
58561
59374
  history: { fields: {
58562
- percentage: {
59375
+ level: {
58563
59376
  kind: "gauge",
58564
59377
  unit: "%"
58565
59378
  },
58566
- volts: {
59379
+ voltage: {
58567
59380
  kind: "gauge",
58568
59381
  unit: "V"
58569
59382
  }
@@ -58623,14 +59436,14 @@ var binaryCapability = {
58623
59436
  * controls to this single-method surface.
58624
59437
  *
58625
59438
  * The cap is intentionally minimal: a single `setBrightness({deviceId,
58626
- * percentage})` mutation plus the auto-injected `getStatus`. Drivers
59439
+ * level})` mutation plus the auto-injected `getStatus`. Drivers
58627
59440
  * that expose richer controls (color temperature, scenes, schedules)
58628
59441
  * should surface those via the device's `getSettingsUISchema()`
58629
59442
  * instead of bloating this cap.
58630
59443
  */
58631
59444
  var BrightnessStatusSchema = object({
58632
59445
  /** Current level as 0..100 inclusive. Firmware-reported. */
58633
- percentage: number().min(0).max(100).meta(readingMeta("percentage")),
59446
+ level: number().min(0).max(100).meta(readingMeta("percentage")),
58634
59447
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
58635
59448
  lastChangedAt: number()
58636
59449
  });
@@ -58642,7 +59455,7 @@ var brightnessCapability = {
58642
59455
  deviceTypes: [DeviceType.Light],
58643
59456
  methods: { setBrightness: method(object({
58644
59457
  deviceId: number().int().nonnegative(),
58645
- percentage: number().min(0).max(100)
59458
+ level: number().min(0).max(100)
58646
59459
  }), _void(), {
58647
59460
  kind: "mutation",
58648
59461
  auth: "protected"
@@ -58655,7 +59468,7 @@ var brightnessCapability = {
58655
59468
  */
58656
59469
  onBrightnessChanged: { data: object({
58657
59470
  deviceId: number(),
58658
- percentage: number().min(0).max(100),
59471
+ level: number().min(0).max(100),
58659
59472
  lastChangedAt: number()
58660
59473
  }) } },
58661
59474
  status: {
@@ -58678,7 +59491,7 @@ onBrightnessChanged: { data: object({
58678
59491
  /**
58679
59492
  * History (D850): recorded series. Actuator tier: level keeps every change.
58680
59493
  */
58681
- history: { fields: { percentage: {
59494
+ history: { fields: { level: {
58682
59495
  kind: "gauge",
58683
59496
  unit: "%",
58684
59497
  minIntervalMs: 0
@@ -59275,11 +60088,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
59275
60088
  }),
59276
60089
  object({
59277
60090
  mode: literal("mired"),
59278
- mireds: number().int().min(50).max(1e3)
60091
+ colorTemperature: number().int().min(50).max(1e3)
59279
60092
  })
59280
60093
  ]);
59281
60094
  var ColorStatusSchema = object({
59282
- /** Active color mode — which of `rgb` / `hsv` / `mireds` reflects
60095
+ /** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
59283
60096
  * the bulb's current state. */
59284
60097
  mode: _enum([
59285
60098
  "rgb",
@@ -59291,7 +60104,7 @@ var ColorStatusSchema = object({
59291
60104
  /** Populated when `mode === 'hsv'`. */
59292
60105
  hsv: HsvTripletSchema.optional(),
59293
60106
  /** Populated when `mode === 'mired'`. */
59294
- mireds: number().int().optional().meta(readingMeta("colour-temperature")),
60107
+ colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
59295
60108
  /** Ms epoch of the last operator-driven change. */
59296
60109
  lastChangedAt: number()
59297
60110
  });
@@ -59323,7 +60136,7 @@ onColorChanged: { data: object({
59323
60136
  ]),
59324
60137
  rgb: RgbTripletSchema.optional(),
59325
60138
  hsv: HsvTripletSchema.optional(),
59326
- mireds: number().int().optional(),
60139
+ colorTemperature: number().int().optional(),
59327
60140
  lastChangedAt: number()
59328
60141
  }) } },
59329
60142
  status: {
@@ -60200,11 +61013,11 @@ var DishwasherStatusSchema = object({
60200
61013
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
60201
61014
  availablePrograms: array(string()),
60202
61015
  /** Seconds remaining on the active cycle, or null when not running / not known. */
60203
- remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
61016
+ remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60204
61017
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
60205
- delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
61018
+ delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60206
61019
  /** Cycle completion 0..100, or null when not applicable. */
60207
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61020
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60208
61021
  /** True when the door is reported open; null when the door state is not known. */
60209
61022
  doorOpen: boolean().nullable(),
60210
61023
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -60423,14 +61236,14 @@ var dishwasherCapability = {
60423
61236
  runtimeState: DishwasherStatusSchema,
60424
61237
  durability: "session",
60425
61238
  history: { fields: {
60426
- progressPercent: {
61239
+ progress: {
60427
61240
  kind: "gauge",
60428
61241
  unit: "%",
60429
61242
  deadband: 2
60430
61243
  },
60431
61244
  state: { kind: "enum" },
60432
61245
  operationState: { kind: "enum" },
60433
- remainingSeconds: {
61246
+ remaining: {
60434
61247
  kind: "gauge",
60435
61248
  unit: "s",
60436
61249
  minIntervalMs: 6e4
@@ -61021,7 +61834,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
61021
61834
  var FanControlStatusSchema = object({
61022
61835
  /** Active speed as 0..100 inclusive. Null when the device has no
61023
61836
  * speed surface (single-speed fan). */
61024
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61837
+ speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61025
61838
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
61026
61839
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
61027
61840
  percentageStep: number().positive().optional().meta(readingMeta("percentage")),
@@ -61048,7 +61861,7 @@ var fanControlCapability = {
61048
61861
  methods: {
61049
61862
  setPercentage: method(object({
61050
61863
  deviceId: number().int().nonnegative(),
61051
- percentage: number().min(0).max(100)
61864
+ speed: number().min(0).max(100)
61052
61865
  }), _void(), {
61053
61866
  kind: "mutation",
61054
61867
  auth: "protected"
@@ -61081,7 +61894,7 @@ var fanControlCapability = {
61081
61894
  },
61082
61895
  /**
61083
61896
  * Runtime-state slice — mirrored by the kernel. UI fan speed
61084
- * sliders read `percentage` for live updates.
61897
+ * sliders read `speed` for live updates.
61085
61898
  */
61086
61899
  runtimeState: FanControlStatusSchema,
61087
61900
  /**
@@ -61095,7 +61908,7 @@ var fanControlCapability = {
61095
61908
  * History (D850): recorded series. Actuator tier: speed keeps every change.
61096
61909
  */
61097
61910
  history: { fields: {
61098
- percentage: {
61911
+ speed: {
61099
61912
  kind: "gauge",
61100
61913
  unit: "%",
61101
61914
  minIntervalMs: 0
@@ -61284,7 +62097,17 @@ var groupCommandsCapability = {
61284
62097
  scope: "device",
61285
62098
  deviceNative: true,
61286
62099
  mode: "singleton",
61287
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
62100
+ deviceTypes: [
62101
+ DeviceType.Switch,
62102
+ DeviceType.Light,
62103
+ DeviceType.Fan,
62104
+ DeviceType.Cover,
62105
+ DeviceType.Valve,
62106
+ DeviceType.Lock,
62107
+ DeviceType.MediaPlayer,
62108
+ DeviceType.Thermostat,
62109
+ DeviceType.Climate
62110
+ ],
61288
62111
  methods: {},
61289
62112
  status: {
61290
62113
  schema: GroupCommandsSchema,
@@ -61302,6 +62125,49 @@ var groupCommandsCapability = {
61302
62125
  history: "none"
61303
62126
  };
61304
62127
  /**
62128
+ * Whether a helper actuator group counts as active (D944): at least one
62129
+ * included member is, or every included member is. What "active" means is the
62130
+ * type's: a cover is open, a lock is locked, a media player is playing, a
62131
+ * climate device is not off. A group of lights, switches and fans reports
62132
+ * `switch.on` instead; this cap is for the types whose own state is not a
62133
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
62134
+ * empty included set takes the device offline, never `active: false`.
62135
+ */
62136
+ var GroupActiveSchema = object({
62137
+ active: boolean().meta({ label: "Active" }),
62138
+ /** Ms epoch when the slice was last updated. */
62139
+ lastChangedAt: number()
62140
+ });
62141
+ var groupActiveCapability = {
62142
+ name: "group-active",
62143
+ scope: "device",
62144
+ deviceNative: true,
62145
+ mode: "singleton",
62146
+ deviceTypes: [
62147
+ DeviceType.Cover,
62148
+ DeviceType.Valve,
62149
+ DeviceType.Lock,
62150
+ DeviceType.MediaPlayer,
62151
+ DeviceType.Climate,
62152
+ DeviceType.Thermostat
62153
+ ],
62154
+ methods: {},
62155
+ status: {
62156
+ schema: GroupActiveSchema,
62157
+ kind: "push"
62158
+ },
62159
+ runtimeState: GroupActiveSchema,
62160
+ /**
62161
+ * Runtime-state durability: **session** — recomputed from the members on the
62162
+ * next source event; a restored value would outlive the members it named.
62163
+ *
62164
+ * See `RuntimeStateDurability`. Enforced by
62165
+ * `scripts/check-runtime-state-durability.ts`.
62166
+ */
62167
+ durability: "session",
62168
+ history: "none"
62169
+ };
62170
+ /**
61305
62171
  * How many members of a helper group answered (D944). Counts are integers,
61306
62172
  * not measurements: zero included is a real count, never a stand-in reading.
61307
62173
  * The reading itself stays on the output capability. History is none — the
@@ -61332,7 +62198,14 @@ var groupStatusCapability = {
61332
62198
  deviceTypes: [
61333
62199
  DeviceType.Sensor,
61334
62200
  DeviceType.Switch,
61335
- DeviceType.Light
62201
+ DeviceType.Light,
62202
+ DeviceType.Fan,
62203
+ DeviceType.Cover,
62204
+ DeviceType.Valve,
62205
+ DeviceType.Lock,
62206
+ DeviceType.MediaPlayer,
62207
+ DeviceType.Thermostat,
62208
+ DeviceType.Climate
61336
62209
  ],
61337
62210
  methods: {},
61338
62211
  status: {
@@ -61480,7 +62353,7 @@ var humidifierCapability = {
61480
62353
  */
61481
62354
  var HumiditySensorStatusSchema = object({
61482
62355
  /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
61483
- percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
62356
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
61484
62357
  precision: 0,
61485
62358
  label: "Humidity"
61486
62359
  })),
@@ -61509,7 +62382,7 @@ var humiditySensorCapability = {
61509
62382
  /**
61510
62383
  * History (D850): recorded series.
61511
62384
  */
61512
- history: { fields: { percent: {
62385
+ history: { fields: { humidity: {
61513
62386
  kind: "gauge",
61514
62387
  unit: "%",
61515
62388
  deadband: .5
@@ -62149,7 +63022,7 @@ var LawnMowerControlStatusSchema = object({
62149
63022
  batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62150
63023
  /** 0..100 mowing-completion percentage of the current task, or null when no
62151
63024
  * task is active / progress is unavailable. */
62152
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63025
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62153
63026
  /** Current device/event code (dynamic — mostly status, sometimes an error),
62154
63027
  * or null when unknown. */
62155
63028
  currentCode: number().nullable().meta(readingMeta("none")),
@@ -62605,7 +63478,14 @@ var lockControlCapability = {
62605
63478
  }),
62606
63479
  unlock: method(object({
62607
63480
  deviceId: number().int().nonnegative(),
62608
- code: string().min(1).optional()
63481
+ code: string().min(1).optional(),
63482
+ /**
63483
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
63484
+ * with one call is refused unless the caller says it was confirmed. A
63485
+ * single lock ignores it. The refusal lives in the callee, so no
63486
+ * automation or API caller reaches the doors by skipping a dialog.
63487
+ */
63488
+ confirm: boolean().optional()
62609
63489
  }), _void(), {
62610
63490
  kind: "mutation",
62611
63491
  auth: "protected"
@@ -62642,6 +63522,44 @@ var lockControlCapability = {
62642
63522
  volatileStateFields: ["lastChangedAt"]
62643
63523
  };
62644
63524
  /**
63525
+ * A monotonic total between resets (D944). Energy uses `power-meter.energy`
63526
+ * so dashboards keep one series; every other instantaneous reading lands here.
63527
+ * `value` is the time integral in the source quantity's accumulated base
63528
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
63529
+ */
63530
+ var MeterStatusSchema = object({
63531
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
63532
+ lastFetchedAt: number()
63533
+ });
63534
+ var meterCapability = {
63535
+ name: "meter",
63536
+ scope: "device",
63537
+ deviceNative: true,
63538
+ mode: "singleton",
63539
+ deviceTypes: [DeviceType.Sensor],
63540
+ methods: {},
63541
+ status: {
63542
+ schema: MeterStatusSchema,
63543
+ kind: "push"
63544
+ },
63545
+ runtimeState: MeterStatusSchema,
63546
+ /**
63547
+ * Runtime-state durability: **restored** — the total is a cache of the
63548
+ * history integral. A restart shows it only until the history pass
63549
+ * recomputes the open period; it is not an in-memory buffer.
63550
+ *
63551
+ * See `RuntimeStateDurability`. Enforced by
63552
+ * `scripts/check-runtime-state-durability.ts`.
63553
+ */
63554
+ durability: "restored",
63555
+ history: { fields: { value: {
63556
+ kind: "counter",
63557
+ deadband: .001,
63558
+ minIntervalMs: 6e4
63559
+ } } },
63560
+ volatileStateFields: ["lastFetchedAt"]
63561
+ };
63562
+ /**
62645
63563
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
62646
63564
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
62647
63565
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -62698,10 +63616,10 @@ var MediaPlayerStatusSchema = object({
62698
63616
  currentMedia: MediaInfoSchema.nullable(),
62699
63617
  /** Current playback position in ms. Null when not seekable or
62700
63618
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
62701
- positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
63619
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62702
63620
  /** Total duration of the current media in ms. Null when unknown
62703
63621
  * (live stream) or nothing is playing. */
62704
- durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
63622
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62705
63623
  /** Shuffle toggle. Null when no shuffle surface.
62706
63624
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
62707
63625
  shuffle: boolean().nullable(),
@@ -62740,7 +63658,7 @@ var mediaPlayerCapability = {
62740
63658
  }),
62741
63659
  seek: method(object({
62742
63660
  deviceId: number().int().nonnegative(),
62743
- positionMs: number().int().nonnegative()
63661
+ position: number().int().nonnegative()
62744
63662
  }), _void(), {
62745
63663
  kind: "mutation",
62746
63664
  auth: "protected"
@@ -62812,7 +63730,7 @@ var mediaPlayerCapability = {
62812
63730
  /**
62813
63731
  * History (D850): volume (a level an operator or an automation sets; the
62814
63732
  * default 10 s floor folds a slider drag into its end value) and mute.
62815
- * `positionMs` is not recorded: it moves continuously while playing.
63733
+ * `position` is not recorded: it moves continuously while playing.
62816
63734
  */
62817
63735
  history: { fields: {
62818
63736
  volumeLevel: {
@@ -63774,9 +64692,9 @@ var NetworkLinkStatusSchema = object({
63774
64692
  * one whose reading has not landed must not be drawn at 0 %. Consumers
63775
64693
  * SKIP a null rather than coerce it.
63776
64694
  */
63777
- signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64695
+ signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63778
64696
  /** Raw received signal strength in dBm, when the firmware reports one. */
63779
- rssiDbm: number().optional().meta(readingMeta("signal-level")),
64697
+ rssi: number().optional().meta(readingMeta("signal-level")),
63780
64698
  /** Network name of a wireless link, when the firmware reports it. */
63781
64699
  ssid: string().optional(),
63782
64700
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
@@ -63785,7 +64703,7 @@ var NetworkLinkStatusSchema = object({
63785
64703
  /** The slice a provider seeds before its first read: nothing is known yet. */
63786
64704
  var NETWORK_LINK_UNKNOWN = {
63787
64705
  type: "unknown",
63788
- signalPercent: null,
64706
+ signal: null,
63789
64707
  lastUpdated: 0
63790
64708
  };
63791
64709
  /**
@@ -63802,9 +64720,9 @@ function signalPercentFromBars(bars, maxBars) {
63802
64720
  * below is 0 %, -50 dBm and above is 100 %, linear between. Non-finite or
63803
64721
  * positive input is not an RSSI: `null`.
63804
64722
  */
63805
- function signalPercentFromRssi(rssiDbm) {
63806
- if (rssiDbm === void 0 || !Number.isFinite(rssiDbm) || rssiDbm > 0) return null;
63807
- return Math.round((Math.min(-50, Math.max(-100, rssiDbm)) + 100) * 2);
64723
+ function signalPercentFromRssi(rssi) {
64724
+ if (rssi === void 0 || !Number.isFinite(rssi) || rssi > 0) return null;
64725
+ return Math.round((Math.min(-50, Math.max(-100, rssi)) + 100) * 2);
63808
64726
  }
63809
64727
  var networkLinkCapability = {
63810
64728
  name: "network-link",
@@ -63855,13 +64773,13 @@ onStatusChanged: { data: object({
63855
64773
  * History (D850): recorded series.
63856
64774
  */
63857
64775
  history: { fields: {
63858
- signalPercent: {
64776
+ signal: {
63859
64777
  kind: "gauge",
63860
64778
  unit: "%",
63861
64779
  deadband: 3,
63862
64780
  minIntervalMs: 6e4
63863
64781
  },
63864
- rssiDbm: {
64782
+ rssi: {
63865
64783
  kind: "gauge",
63866
64784
  unit: "dBm",
63867
64785
  deadband: 2,
@@ -64652,10 +65570,20 @@ var PetFeederDeviceStatusSchema = _enum([
64652
65570
  ]);
64653
65571
  var gramsPortion = number().int().min(4).max(200);
64654
65572
  var PetFeederStatusSchema = object({
64655
- /** Food currently in the bowl (grams). Null when the device has not
64656
- * reported a reading yet. On dual-hopper models this is the combined
64657
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
65573
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
65574
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
65575
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
65576
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
65577
+ * the device has not reported yet. */
64658
65578
  foodLevel: number().nullable().meta(readingMeta("none")),
65579
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
65580
+ * feeder's own units. Null when the model reports none. */
65581
+ bowlFood: number().nullable().meta(readingMeta("none")),
65582
+ /** True while the pet is eating (`state.eating`). */
65583
+ eating: boolean(),
65584
+ /** Whether `markFoodReplenished` does anything on this model. When false
65585
+ * the client must not offer it. */
65586
+ supportsMarkRefilled: boolean(),
64659
65587
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
64660
65588
  * single-hopper models. */
64661
65589
  food1: number().nullable().meta(readingMeta("none")),
@@ -64663,8 +65591,8 @@ var PetFeederStatusSchema = object({
64663
65591
  * single-hopper models. */
64664
65592
  food2: number().nullable().meta(readingMeta("none")),
64665
65593
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
64666
- * (`device_class: problem`, on = low). True when the bowl is empty /
64667
- * below the feeder's low threshold. */
65594
+ * (`device_class: problem`, on = low). True when the tank reports empty,
65595
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
64668
65596
  lowFood: boolean(),
64669
65597
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
64670
65598
  * device has no battery reading. */
@@ -64732,7 +65660,9 @@ var petFeederCapability = {
64732
65660
  kind: "mutation",
64733
65661
  auth: "protected"
64734
65662
  }),
64735
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
65663
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
65664
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
65665
+ * change any sensor reading. */
64736
65666
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
64737
65667
  kind: "mutation",
64738
65668
  auth: "protected"
@@ -64805,6 +65735,7 @@ var petFeederCapability = {
64805
65735
  */
64806
65736
  history: { fields: {
64807
65737
  foodLevel: { kind: "gauge" },
65738
+ bowlFood: { kind: "gauge" },
64808
65739
  food1: { kind: "gauge" },
64809
65740
  food2: { kind: "gauge" },
64810
65741
  batteryPower: {
@@ -65214,27 +66145,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
65214
66145
  * in real-world devices.
65215
66146
  */
65216
66147
  var PowerMeterStatusSchema = object({
65217
- /** Instantaneous power draw in watts. */
65218
- watts: number().optional().meta(readingMeta("power", { label: "Power" })),
66148
+ /** Instantaneous power draw (watts). */
66149
+ power: number().optional().meta(readingMeta("power", { label: "Power" })),
65219
66150
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
65220
- kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
65221
- /** Voltage in volts. */
65222
- volts: number().optional().meta(readingMeta("voltage")),
66151
+ energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
66152
+ /** Voltage (volts). */
66153
+ voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
65223
66154
  /** Current in amperes. */
65224
- amps: number().optional().meta(readingMeta("current")),
66155
+ current: number().optional().meta(readingMeta("current", { label: "Current" })),
65225
66156
  /** Apparent power in volt-amperes. */
65226
- va: number().optional().meta(readingMeta("apparent-power")),
66157
+ apparentPower: number().optional().meta(readingMeta("apparent-power")),
65227
66158
  /** Power factor (0..1) if reported. */
65228
66159
  powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
65229
66160
  /** Ms epoch when the slice was last updated. */
65230
66161
  lastFetchedAt: number()
65231
66162
  });
66163
+ /**
66164
+ * Loads that draw metered mains power (D951). The reading is a feature of the
66165
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
66166
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
66167
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
66168
+ * camera and the like are left out: their draw is not what anyone meters.
66169
+ */
66170
+ var POWER_METER_DEVICE_TYPES = [
66171
+ DeviceType.Sensor,
66172
+ DeviceType.Switch,
66173
+ DeviceType.Light,
66174
+ DeviceType.Fan,
66175
+ DeviceType.Climate,
66176
+ DeviceType.Thermostat,
66177
+ DeviceType.Humidifier,
66178
+ DeviceType.WaterHeater,
66179
+ DeviceType.MediaPlayer,
66180
+ DeviceType.Washer,
66181
+ DeviceType.Dryer,
66182
+ DeviceType.Dishwasher,
66183
+ DeviceType.Vacuum,
66184
+ DeviceType.LawnMower,
66185
+ DeviceType.PetFeeder
66186
+ ];
66187
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
66188
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
66189
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
66190
+ function powerTarget(id, unit) {
66191
+ return {
66192
+ id,
66193
+ cardinality: "scalar",
66194
+ vocabulary: {
66195
+ cap: "power-meter",
66196
+ stateField: id
66197
+ },
66198
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
66199
+ apply: "add",
66200
+ path: id,
66201
+ sources: [{
66202
+ kind: "numeric",
66203
+ requiredUnit: unit,
66204
+ transforms: [{ kind: "identity" }]
66205
+ }],
66206
+ companions: [{
66207
+ kind: "clock",
66208
+ path: "lastFetchedAt",
66209
+ when: "always"
66210
+ }],
66211
+ sourceReport: { status: "assignment-status" },
66212
+ advancedExpression: true
66213
+ };
66214
+ }
66215
+ /** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
66216
+ var powerMeterCustomize = {
66217
+ version: 1,
66218
+ targets: [
66219
+ powerTarget("power", "W"),
66220
+ powerTarget("energy", "kWh"),
66221
+ powerTarget("voltage", "V"),
66222
+ powerTarget("current", "A")
66223
+ ]
66224
+ };
65232
66225
  var powerMeterCapability = {
65233
66226
  name: "power-meter",
65234
66227
  scope: "device",
65235
66228
  deviceNative: true,
65236
66229
  mode: "singleton",
65237
- deviceTypes: [DeviceType.Sensor],
66230
+ deviceTypes: POWER_METER_DEVICE_TYPES,
65238
66231
  methods: {},
65239
66232
  status: {
65240
66233
  schema: PowerMeterStatusSchema,
@@ -65242,41 +66235,41 @@ var powerMeterCapability = {
65242
66235
  },
65243
66236
  runtimeState: PowerMeterStatusSchema,
65244
66237
  /**
65245
- * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
66238
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
65246
66239
  *
65247
66240
  * See `RuntimeStateDurability`. Enforced by
65248
66241
  * `scripts/check-runtime-state-durability.ts`.
65249
66242
  */
65250
66243
  durability: "restored",
65251
66244
  /**
65252
- * History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
66245
+ * History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
65253
66246
  */
65254
66247
  history: { fields: {
65255
- watts: {
66248
+ power: {
65256
66249
  kind: "gauge",
65257
66250
  unit: "W",
65258
66251
  deadband: 1,
65259
66252
  minIntervalMs: 3e4
65260
66253
  },
65261
- kwhTotal: {
66254
+ energy: {
65262
66255
  kind: "counter",
65263
66256
  unit: "kWh",
65264
66257
  deadband: .01,
65265
66258
  minIntervalMs: 6e4
65266
66259
  },
65267
- volts: {
66260
+ voltage: {
65268
66261
  kind: "gauge",
65269
66262
  unit: "V",
65270
66263
  deadband: 1,
65271
66264
  minIntervalMs: 3e4
65272
66265
  },
65273
- amps: {
66266
+ current: {
65274
66267
  kind: "gauge",
65275
66268
  unit: "A",
65276
66269
  deadband: .05,
65277
66270
  minIntervalMs: 3e4
65278
66271
  },
65279
- va: {
66272
+ apparentPower: {
65280
66273
  kind: "gauge",
65281
66274
  unit: "VA",
65282
66275
  deadband: 1,
@@ -65291,7 +66284,8 @@ var powerMeterCapability = {
65291
66284
  } },
65292
66285
  /** Clock fields: written, but excluded from the compare that decides
65293
66286
  * whether persisting is worth a SQLite commit. */
65294
- volatileStateFields: ["lastFetchedAt"]
66287
+ volatileStateFields: ["lastFetchedAt"],
66288
+ customize: powerMeterCustomize
65295
66289
  };
65296
66290
  /**
65297
66291
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -65325,9 +66319,6 @@ var PresenceStatusSchema = object({
65325
66319
  location: string().nullable(),
65326
66320
  /** GPS coordinates when available. Null otherwise. */
65327
66321
  gps: GpsLocationSchema.nullable(),
65328
- /** Optional battery level of the tracking device (0..100). Null
65329
- * when not reported. */
65330
- batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
65331
66322
  /** Ms epoch when the slice was last updated. */
65332
66323
  lastChangedAt: number()
65333
66324
  });
@@ -65356,14 +66347,7 @@ var presenceCapability = {
65356
66347
  * `scripts/check-runtime-state-durability.ts`.
65357
66348
  */
65358
66349
  durability: "restored",
65359
- /**
65360
- * History (D850): recorded series.
65361
- */
65362
- history: { fields: { batteryPercent: {
65363
- kind: "gauge",
65364
- unit: "%",
65365
- deadband: 1
65366
- } } },
66350
+ history: "none",
65367
66351
  /** Clock fields: written, but excluded from the compare that decides
65368
66352
  * whether persisting is worth a SQLite commit. */
65369
66353
  volatileStateFields: ["lastChangedAt"]
@@ -65378,7 +66362,7 @@ var presenceCapability = {
65378
66362
  */
65379
66363
  var PressureSensorStatusSchema = object({
65380
66364
  /** Current pressure in hPa. `null` when the sample was refused (D393). */
65381
- hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
66365
+ pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
65382
66366
  /** Ms epoch when the slice was last updated. */
65383
66367
  lastFetchedAt: number()
65384
66368
  });
@@ -65404,7 +66388,7 @@ var pressureSensorCapability = {
65404
66388
  /**
65405
66389
  * History (D850): recorded series.
65406
66390
  */
65407
- history: { fields: { hpa: {
66391
+ history: { fields: { pressure: {
65408
66392
  kind: "gauge",
65409
66393
  unit: "hPa",
65410
66394
  deadband: .1
@@ -66084,6 +67068,8 @@ var runningCustomize = {
66084
67068
  stateField: "running"
66085
67069
  },
66086
67070
  deviceTypes: [
67071
+ DeviceType.Washer,
67072
+ DeviceType.Dryer,
66087
67073
  DeviceType.Dishwasher,
66088
67074
  DeviceType.Vacuum,
66089
67075
  DeviceType.LawnMower,
@@ -68965,7 +69951,8 @@ var switchCapability = {
68965
69951
  deviceTypes: [
68966
69952
  DeviceType.Switch,
68967
69953
  DeviceType.Siren,
68968
- DeviceType.Light
69954
+ DeviceType.Light,
69955
+ DeviceType.Fan
68969
69956
  ],
68970
69957
  methods: { setState: method(object({
68971
69958
  deviceId: number(),
@@ -69604,7 +70591,7 @@ var tamperCapability = {
69604
70591
  */
69605
70592
  var TemperatureSensorStatusSchema = object({
69606
70593
  /** Current temperature in Celsius. `null` when the sample was refused (D393). */
69607
- celsius: number().nullable().meta(readingMeta("temperature", {
70594
+ temperature: number().nullable().meta(readingMeta("temperature", {
69608
70595
  precision: 1,
69609
70596
  label: "Temperature"
69610
70597
  })),
@@ -69633,7 +70620,7 @@ var temperatureSensorCapability = {
69633
70620
  /**
69634
70621
  * History (D850): recorded series.
69635
70622
  */
69636
- history: { fields: { celsius: {
70623
+ history: { fields: { temperature: {
69637
70624
  kind: "gauge",
69638
70625
  unit: "°C",
69639
70626
  deadband: .1
@@ -69930,7 +70917,7 @@ var VacuumControlStatusSchema = object({
69930
70917
  /** Speed tokens the hardware accepts — drives the UI selector. */
69931
70918
  availableFanSpeeds: array(string()),
69932
70919
  /** 0..100 cleaning-completion percentage of the current task, or null. */
69933
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
70920
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
69934
70921
  /** Current error code (0 / null = no error). */
69935
70922
  errorCode: number().nullable().meta(readingMeta("none")),
69936
70923
  /** Human label for {@link errorCode}, or null when none / undecodable. */
@@ -71311,8 +72298,9 @@ function withClipEventLabels(provider, deps) {
71311
72298
  listClips
71312
72299
  };
71313
72300
  }
72301
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
71314
72302
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
71315
- 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;
72303
+ 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;
71316
72304
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
71317
72305
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
71318
72306
  new Set(CAP_NAMES);
@@ -71656,6 +72644,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
71656
72644
  featureProbe: featureProbeCapability,
71657
72645
  flood: floodCapability,
71658
72646
  gas: gasCapability,
72647
+ groupActive: groupActiveCapability,
71659
72648
  groupCommands: groupCommandsCapability,
71660
72649
  groupStatus: groupStatusCapability,
71661
72650
  heatingValve: heatingValveCapability,
@@ -71667,6 +72656,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
71667
72656
  lawnMowerControl: lawnMowerControlCapability,
71668
72657
  lockControl: lockControlCapability,
71669
72658
  mediaPlayer: mediaPlayerCapability,
72659
+ meter: meterCapability,
71670
72660
  motion: motionCapability,
71671
72661
  motionTrigger: motionTriggerCapability,
71672
72662
  motionZones: motionZonesCapability,
@@ -71729,7 +72719,7 @@ var BaseDevice = class {
71729
72719
  * `device-local-state.ts`. Drivers access via:
71730
72720
  *
71731
72721
  * `this.state.battery.sleeping = true` // patches the battery slice
71732
- * `const pct = this.state.battery.percentage` // reads the battery slice
72722
+ * `const pct = this.state.battery.level` // reads the battery slice
71733
72723
  * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
71734
72724
  *
71735
72725
  * Adding a new cap with `runtimeState:` automatically extends this
@@ -71769,7 +72759,7 @@ var BaseDevice = class {
71769
72759
  * shape; drivers don't redeclare it, they just write through.
71770
72760
  *
71771
72761
  * Read: `this.runtimeState.getCapState('battery')` →
71772
- * `{percentage, charging, sleeping, lastUpdated}` for any
72762
+ * `{level, charging, sleeping, lastUpdated}` for any
71773
72763
  * provider that registers `batteryCapability`.
71774
72764
  * Write: `this.runtimeState.setCapState('battery', { … })`.
71775
72765
  *
@@ -72926,7 +73916,7 @@ var BaseDeviceProvider = class extends BaseAddon {
72926
73916
  return toDeviceSummary(device, this.addonId);
72927
73917
  }
72928
73918
  };
72929
- 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;
73919
+ 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;
72930
73920
  new Set(Object.values(DeviceType));
72931
73921
  Object.freeze({
72932
73922
  "accessories.getStatus": {
@@ -72943,6 +73933,104 @@ Object.freeze({
72943
73933
  access: "create",
72944
73934
  role: "admin"
72945
73935
  },
73936
+ "actions.create": {
73937
+ capName: "actions",
73938
+ capScope: "system",
73939
+ addonId: null,
73940
+ access: "create",
73941
+ role: "admin"
73942
+ },
73943
+ "actions.delete": {
73944
+ capName: "actions",
73945
+ capScope: "system",
73946
+ addonId: null,
73947
+ access: "delete",
73948
+ role: "admin"
73949
+ },
73950
+ "actions.get": {
73951
+ capName: "actions",
73952
+ capScope: "system",
73953
+ addonId: null,
73954
+ access: "view",
73955
+ role: "viewer"
73956
+ },
73957
+ "actions.getLinks": {
73958
+ capName: "actions",
73959
+ capScope: "system",
73960
+ addonId: null,
73961
+ access: "view",
73962
+ role: "admin"
73963
+ },
73964
+ "actions.getTraces": {
73965
+ capName: "actions",
73966
+ capScope: "system",
73967
+ addonId: null,
73968
+ access: "view",
73969
+ role: "admin"
73970
+ },
73971
+ "actions.list": {
73972
+ capName: "actions",
73973
+ capScope: "system",
73974
+ addonId: null,
73975
+ access: "view",
73976
+ role: "admin"
73977
+ },
73978
+ "actions.listForCamera": {
73979
+ capName: "actions",
73980
+ capScope: "system",
73981
+ addonId: null,
73982
+ access: "view",
73983
+ role: "viewer"
73984
+ },
73985
+ "actions.run": {
73986
+ capName: "actions",
73987
+ capScope: "system",
73988
+ addonId: null,
73989
+ access: "create",
73990
+ role: "viewer"
73991
+ },
73992
+ "actions.setLinks": {
73993
+ capName: "actions",
73994
+ capScope: "system",
73995
+ addonId: null,
73996
+ access: "create",
73997
+ role: "admin"
73998
+ },
73999
+ "actions.stop": {
74000
+ capName: "actions",
74001
+ capScope: "system",
74002
+ addonId: null,
74003
+ access: "create",
74004
+ role: "admin"
74005
+ },
74006
+ "actions.update": {
74007
+ capName: "actions",
74008
+ capScope: "system",
74009
+ addonId: null,
74010
+ access: "create",
74011
+ role: "admin"
74012
+ },
74013
+ "actionSource.listActions": {
74014
+ capName: "action-source",
74015
+ capScope: "system",
74016
+ addonId: null,
74017
+ access: "view",
74018
+ role: "admin"
74019
+ },
74020
+ "actionSource.runAction": {
74021
+ capName: "action-source",
74022
+ capScope: "system",
74023
+ addonId: null,
74024
+ access: "create",
74025
+ role: "admin"
74026
+ },
74027
+ "actionSource.stopAction": {
74028
+ capName: "action-source",
74029
+ capScope: "system",
74030
+ addonId: null,
74031
+ access: "create",
74032
+ role: "admin"
74033
+ },
72946
74034
  "addonPages.listPages": {
72947
74035
  capName: "addon-pages",
72948
74036
  capScope: "system",
@@ -74224,6 +75312,13 @@ Object.freeze({
74224
75312
  access: "view",
74225
75313
  role: "admin"
74226
75314
  },
75315
+ "coreBlocks.getAutomationRunState": {
75316
+ capName: "core-blocks",
75317
+ capScope: "system",
75318
+ addonId: null,
75319
+ access: "view",
75320
+ role: "admin"
75321
+ },
74227
75322
  "coreBlocks.getBlockSettings": {
74228
75323
  capName: "core-blocks",
74229
75324
  capScope: "system",
@@ -74245,6 +75340,13 @@ Object.freeze({
74245
75340
  access: "view",
74246
75341
  role: "admin"
74247
75342
  },
75343
+ "coreBlocks.getTraces": {
75344
+ capName: "core-blocks",
75345
+ capScope: "system",
75346
+ addonId: null,
75347
+ access: "view",
75348
+ role: "admin"
75349
+ },
74248
75350
  "coreBlocks.getTypeDefs": {
74249
75351
  capName: "core-blocks",
74250
75352
  capScope: "system",
@@ -74280,6 +75382,13 @@ Object.freeze({
74280
75382
  access: "create",
74281
75383
  role: "admin"
74282
75384
  },
75385
+ "coreBlocks.reportAutomationRun": {
75386
+ capName: "core-blocks",
75387
+ capScope: "system",
75388
+ addonId: null,
75389
+ access: "create",
75390
+ role: "admin"
75391
+ },
74283
75392
  "coreBlocks.restart": {
74284
75393
  capName: "core-blocks",
74285
75394
  capScope: "system",
@@ -76149,6 +77258,13 @@ Object.freeze({
76149
77258
  access: "view",
76150
77259
  role: "viewer"
76151
77260
  },
77261
+ "groupActive.getStatus": {
77262
+ capName: "group-active",
77263
+ capScope: "device",
77264
+ addonId: null,
77265
+ access: "view",
77266
+ role: "viewer"
77267
+ },
76152
77268
  "groupCommands.getStatus": {
76153
77269
  capName: "group-commands",
76154
77270
  capScope: "device",
@@ -77115,6 +78231,13 @@ Object.freeze({
77115
78231
  access: "create",
77116
78232
  role: "admin"
77117
78233
  },
78234
+ "meter.getStatus": {
78235
+ capName: "meter",
78236
+ capScope: "device",
78237
+ addonId: null,
78238
+ access: "view",
78239
+ role: "viewer"
78240
+ },
77118
78241
  "metricsProvider.collectSnapshot": {
77119
78242
  capName: "metrics-provider",
77120
78243
  capScope: "system",
@@ -82839,7 +83962,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82839
83962
  label: t("Full brightness", "Luminosità massima"),
82840
83963
  phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
82841
83964
  icon: "sun",
82842
- args: { percentage: 100 }
83965
+ args: { level: 100 }
82843
83966
  },
82844
83967
  {
82845
83968
  id: "brightness.half",
@@ -82847,7 +83970,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82847
83970
  label: t("Half brightness", "Luminosità a metà"),
82848
83971
  phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
82849
83972
  icon: "sun-dim",
82850
- args: { percentage: 50 }
83973
+ args: { level: 50 }
82851
83974
  },
82852
83975
  {
82853
83976
  id: "brightness.off",
@@ -82855,16 +83978,16 @@ var ACTUATOR_CAP_VOCABULARY = {
82855
83978
  label: t("Brightness off", "Luminosità a zero"),
82856
83979
  phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
82857
83980
  icon: "lightbulb-off",
82858
- args: { percentage: 0 }
83981
+ args: { level: 0 }
82859
83982
  },
82860
83983
  {
82861
83984
  id: "brightness.set",
82862
83985
  method: "setBrightness",
82863
83986
  label: t("Set brightness", "Imposta la luminosità"),
82864
- phrase: t("Set the brightness of {device} to {percentage}", "Imposta la luminosità di {device} a {percentage}"),
83987
+ phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
82865
83988
  icon: "sun-medium",
82866
83989
  openArgs: [{
82867
- key: "percentage",
83990
+ key: "level",
82868
83991
  label: PERCENT,
82869
83992
  offer: {
82870
83993
  kind: "number",
@@ -82878,13 +84001,13 @@ var ACTUATOR_CAP_VOCABULARY = {
82878
84001
  ],
82879
84002
  state: [{
82880
84003
  kind: "numeric",
82881
- field: "percentage",
84004
+ field: "level",
82882
84005
  quantity: t("the brightness", "la luminosità"),
82883
84006
  label: t("Brightness", "Luminosità"),
82884
84007
  unit: "%",
82885
84008
  icon: "sun"
82886
84009
  }],
82887
- events: { onBrightnessChanged: { notOperatorFacing: "the `percentage` state trigger reads the same change" } }
84010
+ events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
82888
84011
  },
82889
84012
  color: {
82890
84013
  scope: "device",
@@ -82898,7 +84021,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82898
84021
  icon: "sun",
82899
84022
  args: { color: {
82900
84023
  mode: "mired",
82901
- mireds: 370
84024
+ colorTemperature: 370
82902
84025
  } }
82903
84026
  },
82904
84027
  {
@@ -82909,7 +84032,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82909
84032
  icon: "snowflake",
82910
84033
  args: { color: {
82911
84034
  mode: "mired",
82912
- mireds: 200
84035
+ colorTemperature: 200
82913
84036
  } }
82914
84037
  },
82915
84038
  {
@@ -83523,7 +84646,7 @@ var ACTUATOR_CAP_VOCABULARY = {
83523
84646
  label: t("Fan off", "Ventilatore spento"),
83524
84647
  phrase: t("Stop {device}", "Ferma {device}"),
83525
84648
  icon: "power-off",
83526
- args: { percentage: 0 }
84649
+ args: { speed: 0 }
83527
84650
  },
83528
84651
  {
83529
84652
  id: "fan.full",
@@ -83531,16 +84654,16 @@ var ACTUATOR_CAP_VOCABULARY = {
83531
84654
  label: t("Fan full", "Ventilatore al massimo"),
83532
84655
  phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
83533
84656
  icon: "fan",
83534
- args: { percentage: 100 }
84657
+ args: { speed: 100 }
83535
84658
  },
83536
84659
  {
83537
84660
  id: "fan.speed",
83538
84661
  method: "setPercentage",
83539
84662
  label: t("Set speed", "Imposta la velocità"),
83540
- phrase: t("Set the speed of {device} to {percentage}", "Imposta la velocità di {device} al {percentage}"),
84663
+ phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
83541
84664
  icon: "fan",
83542
84665
  openArgs: [{
83543
- key: "percentage",
84666
+ key: "speed",
83544
84667
  label: t("Speed", "Velocità"),
83545
84668
  offer: {
83546
84669
  kind: "number",
@@ -83602,7 +84725,7 @@ var ACTUATOR_CAP_VOCABULARY = {
83602
84725
  ],
83603
84726
  state: [{
83604
84727
  kind: "numeric",
83605
- field: "percentage",
84728
+ field: "speed",
83606
84729
  quantity: t("the speed", "la velocità"),
83607
84730
  label: t("Speed", "Velocità"),
83608
84731
  unit: "%",
@@ -84400,7 +85523,7 @@ var APPLIANCE_CAP_VOCABULARY = {
84400
85523
  },
84401
85524
  {
84402
85525
  kind: "numeric",
84403
- field: "progressPercent",
85526
+ field: "progress",
84404
85527
  quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
84405
85528
  label: t("Progress", "Avanzamento"),
84406
85529
  unit: "%",
@@ -84758,10 +85881,10 @@ var APPLIANCE_CAP_VOCABULARY = {
84758
85881
  id: "media.seek",
84759
85882
  method: "seek",
84760
85883
  label: t("Seek", "Vai al punto"),
84761
- phrase: t("Move {device} to {positionMs}", "Porta {device} al punto {positionMs}"),
85884
+ phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
84762
85885
  icon: "fast-forward",
84763
85886
  openArgs: [{
84764
- key: "positionMs",
85887
+ key: "position",
84765
85888
  label: t("Position", "Posizione"),
84766
85889
  offer: {
84767
85890
  kind: "number",
@@ -85213,7 +86336,39 @@ var APPLIANCE_CAP_VOCABULARY = {
85213
86336
  phrase: t("When {device} finishes", "Quando {device} finisce"),
85214
86337
  icon: "square"
85215
86338
  }
85216
- }
86339
+ },
86340
+ overrides: { byType: {
86341
+ [DeviceType.Washer]: {
86342
+ state: { running: {
86343
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
86344
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
86345
+ } },
86346
+ events: {
86347
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
86348
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
86349
+ }
86350
+ },
86351
+ [DeviceType.Dryer]: {
86352
+ state: { running: {
86353
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
86354
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
86355
+ } },
86356
+ events: {
86357
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
86358
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
86359
+ }
86360
+ },
86361
+ [DeviceType.Dishwasher]: {
86362
+ state: { running: {
86363
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
86364
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
86365
+ } },
86366
+ events: {
86367
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
86368
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
86369
+ }
86370
+ }
86371
+ } }
85217
86372
  },
85218
86373
  update: {
85219
86374
  scope: "device",
@@ -85778,7 +86933,7 @@ var SENSOR_CAP_VOCABULARY = {
85778
86933
  state: [
85779
86934
  {
85780
86935
  kind: "numeric",
85781
- field: "co2Ppm",
86936
+ field: "co2",
85782
86937
  quantity: t("the CO₂", "la CO₂"),
85783
86938
  label: t("CO₂", "CO₂"),
85784
86939
  unit: "ppm",
@@ -85786,7 +86941,7 @@ var SENSOR_CAP_VOCABULARY = {
85786
86941
  },
85787
86942
  {
85788
86943
  kind: "numeric",
85789
- field: "vocPpb",
86944
+ field: "voc",
85790
86945
  quantity: t("the VOC level", "il livello di COV"),
85791
86946
  label: t("VOC", "COV"),
85792
86947
  unit: "ppb",
@@ -85822,7 +86977,7 @@ var SENSOR_CAP_VOCABULARY = {
85822
86977
  noun: t("Light level", "Luminosità ambiente"),
85823
86978
  state: [{
85824
86979
  kind: "numeric",
85825
- field: "lux",
86980
+ field: "illuminance",
85826
86981
  quantity: t("the light level", "la luminosità"),
85827
86982
  label: t("Illuminance", "Illuminamento"),
85828
86983
  unit: "lx",
@@ -85985,6 +87140,10 @@ var SENSOR_CAP_VOCABULARY = {
85985
87140
  icon: "flame"
85986
87141
  }]
85987
87142
  },
87143
+ "group-active": {
87144
+ scope: "device",
87145
+ noun: t("Group state", "Stato del gruppo")
87146
+ },
85988
87147
  "group-commands": {
85989
87148
  scope: "device",
85990
87149
  noun: t("Group command", "Comando di gruppo")
@@ -86021,7 +87180,7 @@ var SENSOR_CAP_VOCABULARY = {
86021
87180
  noun: t("Humidity", "Umidità"),
86022
87181
  state: [{
86023
87182
  kind: "numeric",
86024
- field: "percent",
87183
+ field: "humidity",
86025
87184
  quantity: t("the humidity", "l’umidità"),
86026
87185
  label: t("Humidity", "Umidità"),
86027
87186
  unit: "%",
@@ -86041,6 +87200,17 @@ var SENSOR_CAP_VOCABULARY = {
86041
87200
  }],
86042
87201
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
86043
87202
  },
87203
+ meter: {
87204
+ scope: "device",
87205
+ noun: t("Meter", "Contatore"),
87206
+ state: [{
87207
+ kind: "numeric",
87208
+ field: "value",
87209
+ quantity: t("the total", "il totale"),
87210
+ label: t("Total", "Totale"),
87211
+ icon: "gauge"
87212
+ }]
87213
+ },
86044
87214
  "numeric-sensor": {
86045
87215
  scope: "device",
86046
87216
  noun: t("Sensor", "Sensore"),
@@ -86058,7 +87228,7 @@ var SENSOR_CAP_VOCABULARY = {
86058
87228
  state: [
86059
87229
  {
86060
87230
  kind: "numeric",
86061
- field: "watts",
87231
+ field: "power",
86062
87232
  quantity: t("the power draw", "il consumo"),
86063
87233
  label: t("Power", "Potenza"),
86064
87234
  unit: "W",
@@ -86066,7 +87236,7 @@ var SENSOR_CAP_VOCABULARY = {
86066
87236
  },
86067
87237
  {
86068
87238
  kind: "numeric",
86069
- field: "kwhTotal",
87239
+ field: "energy",
86070
87240
  quantity: t("the energy used", "l’energia consumata"),
86071
87241
  label: t("Energy", "Energia"),
86072
87242
  unit: "kWh",
@@ -86074,7 +87244,7 @@ var SENSOR_CAP_VOCABULARY = {
86074
87244
  },
86075
87245
  {
86076
87246
  kind: "numeric",
86077
- field: "volts",
87247
+ field: "voltage",
86078
87248
  quantity: t("the voltage", "la tensione"),
86079
87249
  label: t("Voltage", "Tensione"),
86080
87250
  unit: "V",
@@ -86082,7 +87252,7 @@ var SENSOR_CAP_VOCABULARY = {
86082
87252
  },
86083
87253
  {
86084
87254
  kind: "numeric",
86085
- field: "amps",
87255
+ field: "current",
86086
87256
  quantity: t("the current", "la corrente"),
86087
87257
  label: t("Current", "Corrente"),
86088
87258
  unit: "A",
@@ -86099,13 +87269,6 @@ var SENSOR_CAP_VOCABULARY = {
86099
87269
  label: t("Where", "Posizione"),
86100
87270
  phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
86101
87271
  icon: "map-pin"
86102
- }, {
86103
- kind: "numeric",
86104
- field: "batteryPercent",
86105
- quantity: t("the phone battery", "la batteria del telefono"),
86106
- label: t("Battery", "Batteria"),
86107
- unit: "%",
86108
- icon: "battery"
86109
87272
  }]
86110
87273
  },
86111
87274
  "pressure-sensor": {
@@ -86113,7 +87276,7 @@ var SENSOR_CAP_VOCABULARY = {
86113
87276
  noun: t("Pressure", "Pressione"),
86114
87277
  state: [{
86115
87278
  kind: "numeric",
86116
- field: "hpa",
87279
+ field: "pressure",
86117
87280
  quantity: t("the pressure", "la pressione"),
86118
87281
  label: t("Pressure", "Pressione"),
86119
87282
  unit: "hPa",
@@ -86149,7 +87312,7 @@ var SENSOR_CAP_VOCABULARY = {
86149
87312
  noun: t("Temperature", "Temperatura"),
86150
87313
  state: [{
86151
87314
  kind: "numeric",
86152
- field: "celsius",
87315
+ field: "temperature",
86153
87316
  quantity: t("the temperature", "la temperatura"),
86154
87317
  label: t("Temperature", "Temperatura"),
86155
87318
  unit: "°C",
@@ -86220,7 +87383,7 @@ var SENSOR_CAP_VOCABULARY = {
86220
87383
  state: [
86221
87384
  {
86222
87385
  kind: "numeric",
86223
- field: "percentage",
87386
+ field: "level",
86224
87387
  quantity: t("the battery level", "il livello della batteria"),
86225
87388
  label: t("Battery", "Batteria"),
86226
87389
  unit: "%",
@@ -86262,7 +87425,7 @@ var SENSOR_CAP_VOCABULARY = {
86262
87425
  icon: "battery-charging"
86263
87426
  }
86264
87427
  ],
86265
- events: { onStatusChanged: { notOperatorFacing: "the percentage / sleeping / charging state triggers each read one field of this change" } },
87428
+ events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
86266
87429
  extraEvents: [{
86267
87430
  category: EventCategory.DeviceAwake,
86268
87431
  label: t("Woke up", "Risvegliato"),
@@ -86280,7 +87443,7 @@ var SENSOR_CAP_VOCABULARY = {
86280
87443
  noun: t("Network link", "Collegamento di rete"),
86281
87444
  state: [{
86282
87445
  kind: "numeric",
86283
- field: "signalPercent",
87446
+ field: "signal",
86284
87447
  quantity: t("the signal strength", "l’intensità del segnale"),
86285
87448
  label: t("Signal", "Segnale"),
86286
87449
  unit: "%",
@@ -86373,6 +87536,8 @@ var ML = "model and training machinery — no device action";
86373
87536
  var INTERNAL = "internal plumbing between addons — never operator-facing";
86374
87537
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
86375
87538
  var SYSTEM_CAP_VOCABULARY = {
87539
+ "action-source": system(INTERNAL),
87540
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
86376
87541
  "addon-pages": system(UI_PLUMBING),
86377
87542
  "addon-pages-source": system(UI_PLUMBING),
86378
87543
  "addon-routes": system(UI_PLUMBING),
@@ -86513,8 +87678,47 @@ function projectAction(cap, action) {
86513
87678
  };
86514
87679
  }
86515
87680
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
86516
- 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;
87681
+ 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;
86517
87682
  DeviceFeature.BatteryOperated;
87683
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
87684
+ 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.";
87685
+ 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.";
87686
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
87687
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
87688
+ 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.";
87689
+ function withheld(reason) {
87690
+ return {
87691
+ [DeviceType.Washer]: {
87692
+ exported: false,
87693
+ reason
87694
+ },
87695
+ [DeviceType.Dryer]: {
87696
+ exported: false,
87697
+ reason
87698
+ },
87699
+ [DeviceType.Dishwasher]: {
87700
+ exported: false,
87701
+ reason
87702
+ }
87703
+ };
87704
+ }
87705
+ function offered(received) {
87706
+ return {
87707
+ [DeviceType.Washer]: {
87708
+ exported: true,
87709
+ as: received
87710
+ },
87711
+ [DeviceType.Dryer]: {
87712
+ exported: true,
87713
+ as: received
87714
+ },
87715
+ [DeviceType.Dishwasher]: {
87716
+ exported: true,
87717
+ as: received
87718
+ }
87719
+ };
87720
+ }
87721
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
86518
87722
  var LAST_FETCHED_FIELD = "lastFetchedAt";
86519
87723
  /**
86520
87724
  * How long a bridge stops re-attempting a refresh that did not land.
@@ -86682,6 +87886,31 @@ Object.freeze({
86682
87886
  form: "single",
86683
87887
  optional: false
86684
87888
  }],
87889
+ "actions.get": [{
87890
+ name: "deviceId",
87891
+ form: "single",
87892
+ optional: true
87893
+ }],
87894
+ "actions.getLinks": [{
87895
+ name: "deviceId",
87896
+ form: "single",
87897
+ optional: false
87898
+ }],
87899
+ "actions.listForCamera": [{
87900
+ name: "deviceId",
87901
+ form: "single",
87902
+ optional: false
87903
+ }],
87904
+ "actions.run": [{
87905
+ name: "deviceId",
87906
+ form: "single",
87907
+ optional: true
87908
+ }],
87909
+ "actions.setLinks": [{
87910
+ name: "deviceId",
87911
+ form: "single",
87912
+ optional: false
87913
+ }],
86685
87914
  "addonSettings.getDeviceSettings": [{
86686
87915
  name: "deviceId",
86687
87916
  form: "single",
@@ -87633,6 +88862,11 @@ Object.freeze({
87633
88862
  form: "single",
87634
88863
  optional: false
87635
88864
  }],
88865
+ "groupActive.getStatus": [{
88866
+ name: "deviceId",
88867
+ form: "single",
88868
+ optional: false
88869
+ }],
87636
88870
  "groupCommands.getStatus": [{
87637
88871
  name: "deviceId",
87638
88872
  form: "single",
@@ -87843,6 +89077,11 @@ Object.freeze({
87843
89077
  form: "single",
87844
89078
  optional: false
87845
89079
  }],
89080
+ "meter.getStatus": [{
89081
+ name: "deviceId",
89082
+ form: "single",
89083
+ optional: false
89084
+ }],
87846
89085
  "motion.getStatus": [{
87847
89086
  name: "deviceId",
87848
89087
  form: "single",
@@ -89450,7 +90689,6 @@ Object.freeze({
89450
90689
  "network-access": "ingress",
89451
90690
  "smtp-provider": "email"
89452
90691
  });
89453
- DeviceType.Camera;
89454
90692
  /**
89455
90693
  * A saved Customize assignment (D936). The schema is the type: callers use
89456
90694
  * `z.infer`, they do not restate the shape.
@@ -90805,6 +92043,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
90805
92043
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
90806
92044
  DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
90807
92045
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
92046
+ String.raw`(?<=^|["'(\s>,\[{])`;
90808
92047
  var MB = 1024 * 1024;
90809
92048
  1024 * MB, 3072 * MB;
90810
92049
  //#endregion
@@ -200174,7 +201413,7 @@ var FloodlightAccessory = class extends BaseDevice {
200174
201413
  lastChangedAt: ts
200175
201414
  });
200176
201415
  if (brightness !== void 0) this.setCapSlice(brightnessCapability, {
200177
- percentage: brightness,
201416
+ level: brightness,
200178
201417
  lastChangedAt: ts
200179
201418
  });
200180
201419
  this.hasFetchedFromFirmware = true;
@@ -200293,7 +201532,7 @@ var FloodlightAccessory = class extends BaseDevice {
200293
201532
  }
200294
201533
  brightnessView() {
200295
201534
  return this.getCapSlice(brightnessCapability) ?? {
200296
- percentage: 100,
201535
+ level: 100,
200297
201536
  lastChangedAt: 0
200298
201537
  };
200299
201538
  }
@@ -200318,19 +201557,19 @@ var FloodlightAccessory = class extends BaseDevice {
200318
201557
  registerBrightnessCap() {
200319
201558
  this.ctx.registerNativeCap(brightnessCapability, {
200320
201559
  getStatus: async () => this.brightnessView(),
200321
- setBrightness: async ({ deviceId, percentage }) => {
201560
+ setBrightness: async ({ deviceId, level: percentage }) => {
200322
201561
  if (deviceId !== this.id) return;
200323
201562
  const value = Math.max(0, Math.min(100, percentage));
200324
201563
  if (this.switchView().on) await (await this.api).setWhiteLedState(this.channel, void 0, value);
200325
201564
  this.setCapSlice(brightnessCapability, {
200326
- percentage: value,
201565
+ level: value,
200327
201566
  lastChangedAt: Date.now()
200328
201567
  });
200329
201568
  this.lastSetAtMs = Date.now();
200330
201569
  }
200331
201570
  });
200332
201571
  this.setCapSlice(brightnessCapability, {
200333
- percentage: 100,
201572
+ level: 100,
200334
201573
  lastChangedAt: 0
200335
201574
  });
200336
201575
  }
@@ -201006,13 +202245,13 @@ function mapNetworkReadout(readout, now) {
201006
202245
  const type = linkTypeOf(readout);
201007
202246
  const raw = readout.wifiSignal;
201008
202247
  const wireless = type === "wifi" || type === "cellular";
201009
- const signalPercent = wireless ? signalPercentOf(raw) : null;
202248
+ const signal = wireless ? signalPercentOf(raw) : null;
201010
202249
  const rssiDbm = wireless && raw !== void 0 && Number.isFinite(raw) && raw < 0 ? raw : void 0;
201011
202250
  const ssid = wireless && readout.ssid !== void 0 && readout.ssid !== "" ? readout.ssid : void 0;
201012
202251
  return {
201013
202252
  type,
201014
- signalPercent,
201015
- ...rssiDbm !== void 0 ? { rssiDbm } : {},
202253
+ signal,
202254
+ ...rssiDbm !== void 0 ? { rssi: rssiDbm } : {},
201016
202255
  ...ssid !== void 0 ? { ssid } : {},
201017
202256
  lastUpdated: now
201018
202257
  };
@@ -204255,7 +205494,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
204255
205494
  */
204256
205495
  mapBatteryInfo(info) {
204257
205496
  return {
204258
- percentage: typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.percentage ?? null,
205497
+ level: typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.level ?? null,
204259
205498
  charging: reportsPowerSource(info) ? deriveChargingSource(info.adapterStatus, info.chargeStatus) : this.state.battery.charging ?? "unknown",
204260
205499
  sleeping: info.sleeping === true || this.sleeping,
204261
205500
  lastUpdated: Date.now()
@@ -204391,7 +205630,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
204391
205630
  }
204392
205631
  });
204393
205632
  if (this.getCapSlice(batteryCapability) === null) this.setCapSlice(batteryCapability, {
204394
- percentage: null,
205633
+ level: null,
204395
205634
  charging: "none",
204396
205635
  sleeping: false,
204397
205636
  lastUpdated: Date.now()
@@ -204619,7 +205858,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
204619
205858
  });
204620
205859
  this.setCapSlice(batteryCapability, {
204621
205860
  ...this.state.battery,
204622
- percentage: mapped.percentage,
205861
+ level: mapped.level,
204623
205862
  charging: mapped.charging,
204624
205863
  lastUpdated: Date.now()
204625
205864
  });