@camstack/addon-provider-reolink 1.2.211 → 1.2.212

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.js CHANGED
@@ -5479,7 +5479,7 @@ var ZodIssueCode = {
5479
5479
  var ZodFirstPartyTypeKind;
5480
5480
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5481
5481
  //#endregion
5482
- //#region ../types/dist/sleep-bykKrTgu.mjs
5482
+ //#region ../types/dist/sleep--jXS9hWo.mjs
5483
5483
  /**
5484
5484
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5485
5485
  * window to float samples (D455).
@@ -8710,6 +8710,7 @@ var CAP_NAMES = Object.freeze([
8710
8710
  "filesystem-browse",
8711
8711
  "flood",
8712
8712
  "gas",
8713
+ "group-active",
8713
8714
  "group-commands",
8714
8715
  "group-status",
8715
8716
  "heating-valve",
@@ -8732,6 +8733,7 @@ var CAP_NAMES = Object.freeze([
8732
8733
  "login-method",
8733
8734
  "media-player",
8734
8735
  "mesh-network",
8736
+ "meter",
8735
8737
  "metrics-provider",
8736
8738
  "model-convert",
8737
8739
  "model-distributor",
@@ -9530,6 +9532,10 @@ Object.freeze({
9530
9532
  "path": "detected",
9531
9533
  "valueType": "boolean"
9532
9534
  }],
9535
+ "group-active": [{
9536
+ "path": "active",
9537
+ "valueType": "boolean"
9538
+ }],
9533
9539
  "group-status": [
9534
9540
  {
9535
9541
  "path": "included",
@@ -9789,6 +9795,10 @@ Object.freeze({
9789
9795
  ]
9790
9796
  }
9791
9797
  ],
9798
+ "meter": [{
9799
+ "path": "value",
9800
+ "valueType": "number"
9801
+ }],
9792
9802
  "motion": [
9793
9803
  {
9794
9804
  "path": "detected",
@@ -10570,7 +10580,7 @@ function sleep$2(ms) {
10570
10580
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
10571
10581
  }
10572
10582
  //#endregion
10573
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10583
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10574
10584
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10575
10585
  DeviceType["Camera"] = "camera";
10576
10586
  DeviceType["Hub"] = "hub";
@@ -10664,8 +10674,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10664
10674
  DeviceType["PetFeeder"] = "pet-feeder";
10665
10675
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10666
10676
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10667
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10677
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10678
+ * A plug-built dishwasher carries running-state and power instead. */
10668
10679
  DeviceType["Dishwasher"] = "dishwasher";
10680
+ /** Washing machine. A plug-built one is running while its power stays
10681
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10682
+ DeviceType["Washer"] = "washer";
10683
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10684
+ DeviceType["Dryer"] = "dryer";
10669
10685
  return DeviceType;
10670
10686
  }({});
10671
10687
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -10913,7 +10929,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
10913
10929
  return DeviceRole;
10914
10930
  }({});
10915
10931
  //#endregion
10916
- //#region ../types/dist/composition-OhpuespA.mjs
10932
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
10917
10933
  /**
10918
10934
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10919
10935
  *
@@ -11228,6 +11244,13 @@ var table$1 = {
11228
11244
  apply: () => {
11229
11245
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11230
11246
  }
11247
+ },
11248
+ schmitt: {
11249
+ minArgs: 3,
11250
+ maxArgs: 5,
11251
+ apply: () => {
11252
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11253
+ }
11231
11254
  }
11232
11255
  };
11233
11256
  Object.freeze(Object.assign(Object.create(null), table$1));
@@ -11669,6 +11692,99 @@ function validateExpressionSource(src) {
11669
11692
  return null;
11670
11693
  }
11671
11694
  /**
11695
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11696
+ *
11697
+ * Standby of the control board is under 5 W; a motor or a heater is far
11698
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11699
+ * a cycle, so running starts only after 30 s above the line.
11700
+ *
11701
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11702
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11703
+ * report "finished" in the middle of the program, so the stop dwell is
11704
+ * 10 minutes on every type. The number is stored per type so one machine
11705
+ * can move later without moving the others.
11706
+ */
11707
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11708
+ var APPLIANCE_KINDS = [
11709
+ "washer",
11710
+ "dryer",
11711
+ "dishwasher"
11712
+ ];
11713
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11714
+ /**
11715
+ * Statistics and integrals over a history read (D944).
11716
+ *
11717
+ * Pure: the same points always give the same answer, so a restart recomputes
11718
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11719
+ * never the mean of the samples. A hole with no held value is not integrated
11720
+ * as zero. A truncated read is not published.
11721
+ */
11722
+ var STATISTIC_STATS = [
11723
+ "min",
11724
+ "max",
11725
+ "mean",
11726
+ "sum",
11727
+ "count",
11728
+ "change"
11729
+ ];
11730
+ /**
11731
+ * The range a statistics or integral helper asks history for (D944).
11732
+ *
11733
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11734
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11735
+ * is strictly longer than the longest that range can be. Forever qualifies.
11736
+ * Equal to a tier's retention is a refusal, not a short answer.
11737
+ */
11738
+ var MINUTE_MS = 6e4;
11739
+ var HOUR_MS = 60 * MINUTE_MS;
11740
+ var DAY_MS = 24 * HOUR_MS;
11741
+ var ROLLING_SPANS = [
11742
+ "1h",
11743
+ "24h",
11744
+ "2d",
11745
+ "7d",
11746
+ "30d",
11747
+ "custom"
11748
+ ];
11749
+ var CALENDAR_PERIODS = [
11750
+ "today",
11751
+ "yesterday",
11752
+ "this-week",
11753
+ "this-month"
11754
+ ];
11755
+ var RESET_PERIODS = [
11756
+ "never",
11757
+ "daily",
11758
+ "weekly",
11759
+ "monthly"
11760
+ ];
11761
+ 5 * MINUTE_MS;
11762
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
11763
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
11764
+ var rollingWindowObject = object({
11765
+ kind: literal("rolling"),
11766
+ span: _enum(ROLLING_SPANS),
11767
+ customMinutes: number().int().min(5).optional()
11768
+ }).check((payload) => {
11769
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
11770
+ code: "custom",
11771
+ message: "a custom range needs a duration in minutes",
11772
+ path: ["customMinutes"],
11773
+ input: payload.value
11774
+ });
11775
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
11776
+ code: "custom",
11777
+ message: "only a custom range carries a duration",
11778
+ path: ["customMinutes"],
11779
+ input: payload.value
11780
+ });
11781
+ });
11782
+ var RollingWindowSchema = rollingWindowObject;
11783
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
11784
+ kind: literal("calendar"),
11785
+ period: _enum(CALENDAR_PERIODS)
11786
+ })]);
11787
+ /**
11672
11788
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11673
11789
  * id. Split out so helper schemas can name it without a cycle through
11674
11790
  * `composition.ts` (D659).
@@ -11694,7 +11810,13 @@ var GROUP_TEMPLATE_IDS = [
11694
11810
  "occupancy",
11695
11811
  "binary",
11696
11812
  "switch",
11697
- "light"
11813
+ "light",
11814
+ "fan",
11815
+ "cover",
11816
+ "valve",
11817
+ "lock",
11818
+ "media-player",
11819
+ "climate"
11698
11820
  ];
11699
11821
  var NUMERIC_AGGREGATES = [
11700
11822
  "mean",
@@ -11724,14 +11846,73 @@ var HelperMemberSchema = object({
11724
11846
  cap: string().min(1),
11725
11847
  fieldPath: string().min(1)
11726
11848
  });
11727
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11728
- kind: literal("group"),
11729
- templateId: GroupTemplateIdSchema,
11730
- aggregate: AggregateReducerSchema,
11731
- members: array(HelperMemberSchema).min(1).max(64),
11732
- /** Actuator groups only. Readings leave this unset. */
11733
- active: ActiveCriterionSchema.optional()
11734
- })]);
11849
+ var HelperSpecSchema = discriminatedUnion("kind", [
11850
+ object({
11851
+ kind: literal("group"),
11852
+ templateId: GroupTemplateIdSchema,
11853
+ aggregate: AggregateReducerSchema,
11854
+ members: array(HelperMemberSchema).min(1).max(64),
11855
+ /** Actuator groups only. Readings leave this unset. */
11856
+ active: ActiveCriterionSchema.optional()
11857
+ }),
11858
+ object({
11859
+ kind: literal("appliance"),
11860
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
11861
+ appliance: _enum(APPLIANCE_KINDS),
11862
+ thresholdWatts: number().positive(),
11863
+ startDwellMs: number().int().nonnegative(),
11864
+ stopDwellMs: number().int().nonnegative(),
11865
+ member: HelperMemberSchema
11866
+ }),
11867
+ object({
11868
+ kind: literal("statistic"),
11869
+ stat: _enum(STATISTIC_STATS),
11870
+ window: StatisticWindowSchema,
11871
+ source: HelperMemberSchema
11872
+ }),
11873
+ object({
11874
+ kind: literal("integral"),
11875
+ reset: _enum(RESET_PERIODS),
11876
+ output: _enum(["energy", "meter"]),
11877
+ source: HelperMemberSchema
11878
+ }),
11879
+ object({
11880
+ kind: literal("threshold"),
11881
+ source: HelperMemberSchema,
11882
+ high: number(),
11883
+ low: number(),
11884
+ onForMs: number().int().nonnegative(),
11885
+ offForMs: number().int().nonnegative()
11886
+ }),
11887
+ object({
11888
+ kind: literal("arithmetic"),
11889
+ op: _enum([
11890
+ "sum",
11891
+ "difference",
11892
+ "ratio"
11893
+ ]),
11894
+ inputs: array(HelperMemberSchema).min(2).max(64)
11895
+ }),
11896
+ object({
11897
+ kind: literal("expression"),
11898
+ expr: string().min(1),
11899
+ inputs: array(object({
11900
+ name: string().min(1),
11901
+ member: HelperMemberSchema
11902
+ })).min(1).max(64),
11903
+ output: _enum(["number", "boolean"])
11904
+ }),
11905
+ object({
11906
+ kind: literal("derivative"),
11907
+ window: RollingWindowSchema,
11908
+ per: _enum([
11909
+ "second",
11910
+ "minute",
11911
+ "hour"
11912
+ ]),
11913
+ source: HelperMemberSchema
11914
+ })
11915
+ ]);
11735
11916
  /** A composed device's stableId is this prefix plus the block id. */
11736
11917
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11737
11918
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -11792,6 +11973,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11792
11973
  "all"
11793
11974
  ]),
11794
11975
  reads: array(CompositionFieldReadSchema).min(1).max(64),
11976
+ /**
11977
+ * Set when the reads are an enum state, not a yes/no: the listed states
11978
+ * count as active (a cover is `open`). A state outside the list is inactive;
11979
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
11980
+ */
11981
+ activeWhen: array(string().min(1)).min(1).optional(),
11795
11982
  /** `value` is the reading; the others are the member counts. */
11796
11983
  publish: _enum([
11797
11984
  "value",
@@ -11805,6 +11992,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11805
11992
  "omit",
11806
11993
  "offline"
11807
11994
  ])
11995
+ }),
11996
+ object({
11997
+ kind: literal("series"),
11998
+ read: CompositionFieldReadSchema,
11999
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12000
+ publish: _enum([
12001
+ "value",
12002
+ "included",
12003
+ "excluded",
12004
+ "total"
12005
+ ]),
12006
+ /**
12007
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12008
+ * device unseeded until history answers, so a restart cannot publish 0.
12009
+ */
12010
+ empty: _enum([
12011
+ "null",
12012
+ "omit",
12013
+ "offline"
12014
+ ])
11808
12015
  })
11809
12016
  ]);
11810
12017
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -17534,7 +17741,22 @@ var BrokerProviderInfoSchema = object({
17534
17741
  */
17535
17742
  interactiveConnectPath: string().min(1).optional(),
17536
17743
  /** Relative hub path to disconnect the linked account (same popup pattern). */
17537
- interactiveDisconnectPath: string().min(1).optional()
17744
+ interactiveDisconnectPath: string().min(1).optional(),
17745
+ /**
17746
+ * The kind signs in through a server-held interactive session instead of
17747
+ * (or before) typed credentials. `basePath` is a hub-relative route set
17748
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
17749
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
17750
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
17751
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
17752
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
17753
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
17754
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
17755
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
17756
+ * offers it as the default path; the fields the session fills are
17757
+ * marked `filledBySignIn` in the kind's settings schema.
17758
+ */
17759
+ signIn: object({ basePath: string().min(1) }).optional()
17538
17760
  }))
17539
17761
  });
17540
17762
  var ListInputSchema = object({
@@ -18639,7 +18861,8 @@ var CompositionProblemSchema = object({
18639
18861
  "native-capability-replace",
18640
18862
  "mixed-quantity",
18641
18863
  "helper-mismatch",
18642
- "member-constraint"
18864
+ "member-constraint",
18865
+ "retention"
18643
18866
  ]),
18644
18867
  path: string(),
18645
18868
  message: string()
@@ -18873,13 +19096,17 @@ Object.freeze({
18873
19096
  latchedAt: {
18874
19097
  kind: "fixed",
18875
19098
  result: ["number", "null"]
19099
+ },
19100
+ schmitt: {
19101
+ kind: "fixed",
19102
+ result: ["boolean", "null"]
18876
19103
  }
18877
19104
  });
18878
19105
  /**
18879
19106
  * One catalog row per base reading a group helper can build (D944 §3).
18880
19107
  * Data, not a form default written into the block.
18881
19108
  */
18882
- var NOW = {
19109
+ var NOW$2 = {
18883
19110
  fieldPath: "lastFetchedAt",
18884
19111
  expr: "now"
18885
19112
  };
@@ -18893,6 +19120,8 @@ function numeric(input) {
18893
19120
  family: "numeric",
18894
19121
  deviceType: DeviceType.Sensor,
18895
19122
  memberDeviceType: DeviceType.Sensor,
19123
+ memberDeviceTypes: [DeviceType.Sensor],
19124
+ activeWhen: void 0,
18896
19125
  commandCaps: [],
18897
19126
  baseUnit: baseUnitOf(input.quantity),
18898
19127
  aggregates: NUMERIC_AGGREGATES,
@@ -18908,6 +19137,8 @@ function binary(input) {
18908
19137
  family: "binary",
18909
19138
  deviceType: DeviceType.Sensor,
18910
19139
  memberDeviceType: DeviceType.Sensor,
19140
+ memberDeviceTypes: [DeviceType.Sensor],
19141
+ activeWhen: void 0,
18911
19142
  commandCaps: [],
18912
19143
  quantity: "none",
18913
19144
  baseUnit: void 0,
@@ -18925,7 +19156,7 @@ var GROUP_TEMPLATES = [
18925
19156
  quantity: "temperature",
18926
19157
  defaultAggregate: "mean",
18927
19158
  empty: "null",
18928
- companions: [NOW]
19159
+ companions: [NOW$2]
18929
19160
  }),
18930
19161
  numeric({
18931
19162
  id: "humidity",
@@ -18936,7 +19167,7 @@ var GROUP_TEMPLATES = [
18936
19167
  quantity: "humidity",
18937
19168
  defaultAggregate: "mean",
18938
19169
  empty: "null",
18939
- companions: [NOW]
19170
+ companions: [NOW$2]
18940
19171
  }),
18941
19172
  numeric({
18942
19173
  id: "pressure",
@@ -18947,7 +19178,7 @@ var GROUP_TEMPLATES = [
18947
19178
  quantity: "pressure",
18948
19179
  defaultAggregate: "mean",
18949
19180
  empty: "null",
18950
- companions: [NOW]
19181
+ companions: [NOW$2]
18951
19182
  }),
18952
19183
  numeric({
18953
19184
  id: "illuminance",
@@ -18958,7 +19189,7 @@ var GROUP_TEMPLATES = [
18958
19189
  quantity: "illuminance",
18959
19190
  defaultAggregate: "mean",
18960
19191
  empty: "null",
18961
- companions: [NOW]
19192
+ companions: [NOW$2]
18962
19193
  }),
18963
19194
  numeric({
18964
19195
  id: "power",
@@ -18969,7 +19200,7 @@ var GROUP_TEMPLATES = [
18969
19200
  quantity: "power",
18970
19201
  defaultAggregate: "sum",
18971
19202
  empty: "omit",
18972
- companions: [NOW]
19203
+ companions: [NOW$2]
18973
19204
  }),
18974
19205
  numeric({
18975
19206
  id: "energy",
@@ -18980,7 +19211,7 @@ var GROUP_TEMPLATES = [
18980
19211
  quantity: "energy",
18981
19212
  defaultAggregate: "sum",
18982
19213
  empty: "omit",
18983
- companions: [NOW]
19214
+ companions: [NOW$2]
18984
19215
  }),
18985
19216
  numeric({
18986
19217
  id: "battery",
@@ -19015,7 +19246,7 @@ var GROUP_TEMPLATES = [
19015
19246
  quantity: "device",
19016
19247
  defaultAggregate: "mean",
19017
19248
  empty: "null",
19018
- companions: [NOW]
19249
+ companions: [NOW$2]
19019
19250
  }),
19020
19251
  binary({
19021
19252
  id: "motion",
@@ -19109,6 +19340,11 @@ var GROUP_TEMPLATES = [
19109
19340
  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.",
19110
19341
  deviceType: DeviceType.Switch,
19111
19342
  outputCap: "switch",
19343
+ outputField: "on",
19344
+ member: {
19345
+ cap: "switch",
19346
+ fieldPath: "on"
19347
+ },
19112
19348
  commandCaps: ["switch"]
19113
19349
  }),
19114
19350
  actuator({
@@ -19116,34 +19352,136 @@ var GROUP_TEMPLATES = [
19116
19352
  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.",
19117
19353
  deviceType: DeviceType.Light,
19118
19354
  outputCap: "switch",
19355
+ outputField: "on",
19356
+ member: {
19357
+ cap: "switch",
19358
+ fieldPath: "on"
19359
+ },
19119
19360
  commandCaps: [
19120
19361
  "switch",
19121
19362
  "brightness",
19122
19363
  "color"
19123
19364
  ]
19365
+ }),
19366
+ actuator({
19367
+ id: "fan",
19368
+ 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.",
19369
+ deviceType: DeviceType.Fan,
19370
+ outputCap: "switch",
19371
+ outputField: "on",
19372
+ member: {
19373
+ cap: "switch",
19374
+ fieldPath: "on"
19375
+ },
19376
+ commandCaps: ["switch", "fan-control"]
19377
+ }),
19378
+ actuator({
19379
+ id: "cover",
19380
+ 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.",
19381
+ deviceType: DeviceType.Cover,
19382
+ outputCap: "group-active",
19383
+ outputField: "active",
19384
+ member: {
19385
+ cap: "cover",
19386
+ fieldPath: "state"
19387
+ },
19388
+ activeWhen: [
19389
+ "open",
19390
+ "opening",
19391
+ "stopped"
19392
+ ],
19393
+ commandCaps: ["cover"]
19394
+ }),
19395
+ actuator({
19396
+ id: "valve",
19397
+ 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.",
19398
+ deviceType: DeviceType.Valve,
19399
+ outputCap: "group-active",
19400
+ outputField: "active",
19401
+ member: {
19402
+ cap: "valve",
19403
+ fieldPath: "state"
19404
+ },
19405
+ activeWhen: [
19406
+ "open",
19407
+ "opening",
19408
+ "stopped"
19409
+ ],
19410
+ commandCaps: ["valve"]
19411
+ }),
19412
+ actuator({
19413
+ id: "lock",
19414
+ 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.",
19415
+ deviceType: DeviceType.Lock,
19416
+ outputCap: "group-active",
19417
+ outputField: "active",
19418
+ member: {
19419
+ cap: "lock-control",
19420
+ fieldPath: "state"
19421
+ },
19422
+ activeWhen: ["locked"],
19423
+ commandCaps: ["lock-control"]
19424
+ }),
19425
+ actuator({
19426
+ id: "media-player",
19427
+ 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.",
19428
+ deviceType: DeviceType.MediaPlayer,
19429
+ outputCap: "group-active",
19430
+ outputField: "active",
19431
+ member: {
19432
+ cap: "media-player",
19433
+ fieldPath: "state"
19434
+ },
19435
+ activeWhen: ["playing", "buffering"],
19436
+ commandCaps: ["media-player"]
19437
+ }),
19438
+ actuator({
19439
+ id: "climate",
19440
+ 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.",
19441
+ deviceType: DeviceType.Thermostat,
19442
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
19443
+ outputCap: "group-active",
19444
+ outputField: "active",
19445
+ member: {
19446
+ cap: "climate-control",
19447
+ fieldPath: "mode"
19448
+ },
19449
+ activeWhen: [
19450
+ "heat",
19451
+ "cool",
19452
+ "auto",
19453
+ "heat_cool",
19454
+ "fan_only",
19455
+ "dry"
19456
+ ],
19457
+ commandCaps: ["climate-control"]
19124
19458
  })
19125
19459
  ];
19126
19460
  function actuator(input) {
19127
19461
  return {
19128
- ...input,
19462
+ id: input.id,
19463
+ summary: input.summary,
19464
+ deviceType: input.deviceType,
19465
+ outputCap: input.outputCap,
19466
+ outputField: input.outputField,
19467
+ commandCaps: input.commandCaps,
19129
19468
  family: "actuator",
19130
19469
  role: void 0,
19131
19470
  memberDeviceType: input.deviceType,
19132
- outputField: "on",
19471
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
19472
+ activeWhen: input.activeWhen,
19133
19473
  quantity: "none",
19134
19474
  baseUnit: void 0,
19135
19475
  aggregates: BINARY_AGGREGATES,
19136
19476
  defaultAggregate: "any",
19137
19477
  empty: "offline",
19138
- accept: [{
19139
- cap: "switch",
19140
- fieldPath: "on"
19141
- }],
19478
+ accept: [input.member],
19142
19479
  companions: [CHANGED]
19143
19480
  };
19144
19481
  }
19145
19482
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
19146
19483
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
19484
+ DeviceType.Camera;
19147
19485
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19148
19486
  /**
19149
19487
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -44503,7 +44841,7 @@ var NetworkAccessStatusSchema = object({
44503
44841
  });
44504
44842
  /**
44505
44843
  * Optional, richer endpoint shape returned by providers that expose
44506
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
44844
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44507
44845
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44508
44846
  * the originating provider config (mode + sourcePort) so the
44509
44847
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -47190,7 +47528,20 @@ var NcRuleInputSchema = object({
47190
47528
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47191
47529
  * path does not run Zod defaults.
47192
47530
  */
47193
- groupingSec: number().int().min(5).max(60).optional()
47531
+ groupingSec: number().int().min(5).max(60).optional(),
47532
+ /**
47533
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47534
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47535
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47536
+ * same mark the Library writes, and records whose notification it was and
47537
+ * when. A channel with no buttons gets the same two choices as links.
47538
+ *
47539
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47540
+ * cap path): absent and `false` both mean off, which is what every rule
47541
+ * authored before this field meant. An editor turns it off by writing
47542
+ * `false`, so it needs no `clear` key.
47543
+ */
47544
+ askVerdict: boolean().optional()
47194
47545
  });
47195
47546
  /**
47196
47547
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -58432,7 +58783,8 @@ var BATTERY_DEVICE_TYPES = [
58432
58783
  DeviceType.Switch,
58433
58784
  DeviceType.PetFeeder,
58434
58785
  DeviceType.Container,
58435
- DeviceType.Thermostat
58786
+ DeviceType.Thermostat,
58787
+ DeviceType.EventEmitter
58436
58788
  ];
58437
58789
  /** Generic Customize target for the percentage reading (D936). */
58438
58790
  var batteryCustomize = {
@@ -61292,7 +61644,17 @@ var groupCommandsCapability = {
61292
61644
  scope: "device",
61293
61645
  deviceNative: true,
61294
61646
  mode: "singleton",
61295
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
61647
+ deviceTypes: [
61648
+ DeviceType.Switch,
61649
+ DeviceType.Light,
61650
+ DeviceType.Fan,
61651
+ DeviceType.Cover,
61652
+ DeviceType.Valve,
61653
+ DeviceType.Lock,
61654
+ DeviceType.MediaPlayer,
61655
+ DeviceType.Thermostat,
61656
+ DeviceType.Climate
61657
+ ],
61296
61658
  methods: {},
61297
61659
  status: {
61298
61660
  schema: GroupCommandsSchema,
@@ -61310,6 +61672,49 @@ var groupCommandsCapability = {
61310
61672
  history: "none"
61311
61673
  };
61312
61674
  /**
61675
+ * Whether a helper actuator group counts as active (D944): at least one
61676
+ * included member is, or every included member is. What "active" means is the
61677
+ * type's: a cover is open, a lock is locked, a media player is playing, a
61678
+ * climate device is not off. A group of lights, switches and fans reports
61679
+ * `switch.on` instead; this cap is for the types whose own state is not a
61680
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
61681
+ * empty included set takes the device offline, never `active: false`.
61682
+ */
61683
+ var GroupActiveSchema = object({
61684
+ active: boolean().meta({ label: "Active" }),
61685
+ /** Ms epoch when the slice was last updated. */
61686
+ lastChangedAt: number()
61687
+ });
61688
+ var groupActiveCapability = {
61689
+ name: "group-active",
61690
+ scope: "device",
61691
+ deviceNative: true,
61692
+ mode: "singleton",
61693
+ deviceTypes: [
61694
+ DeviceType.Cover,
61695
+ DeviceType.Valve,
61696
+ DeviceType.Lock,
61697
+ DeviceType.MediaPlayer,
61698
+ DeviceType.Climate,
61699
+ DeviceType.Thermostat
61700
+ ],
61701
+ methods: {},
61702
+ status: {
61703
+ schema: GroupActiveSchema,
61704
+ kind: "push"
61705
+ },
61706
+ runtimeState: GroupActiveSchema,
61707
+ /**
61708
+ * Runtime-state durability: **session** — recomputed from the members on the
61709
+ * next source event; a restored value would outlive the members it named.
61710
+ *
61711
+ * See `RuntimeStateDurability`. Enforced by
61712
+ * `scripts/check-runtime-state-durability.ts`.
61713
+ */
61714
+ durability: "session",
61715
+ history: "none"
61716
+ };
61717
+ /**
61313
61718
  * How many members of a helper group answered (D944). Counts are integers,
61314
61719
  * not measurements: zero included is a real count, never a stand-in reading.
61315
61720
  * The reading itself stays on the output capability. History is none — the
@@ -61340,7 +61745,14 @@ var groupStatusCapability = {
61340
61745
  deviceTypes: [
61341
61746
  DeviceType.Sensor,
61342
61747
  DeviceType.Switch,
61343
- DeviceType.Light
61748
+ DeviceType.Light,
61749
+ DeviceType.Fan,
61750
+ DeviceType.Cover,
61751
+ DeviceType.Valve,
61752
+ DeviceType.Lock,
61753
+ DeviceType.MediaPlayer,
61754
+ DeviceType.Thermostat,
61755
+ DeviceType.Climate
61344
61756
  ],
61345
61757
  methods: {},
61346
61758
  status: {
@@ -62613,7 +63025,14 @@ var lockControlCapability = {
62613
63025
  }),
62614
63026
  unlock: method(object({
62615
63027
  deviceId: number().int().nonnegative(),
62616
- code: string().min(1).optional()
63028
+ code: string().min(1).optional(),
63029
+ /**
63030
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
63031
+ * with one call is refused unless the caller says it was confirmed. A
63032
+ * single lock ignores it. The refusal lives in the callee, so no
63033
+ * automation or API caller reaches the doors by skipping a dialog.
63034
+ */
63035
+ confirm: boolean().optional()
62617
63036
  }), _void(), {
62618
63037
  kind: "mutation",
62619
63038
  auth: "protected"
@@ -62650,6 +63069,44 @@ var lockControlCapability = {
62650
63069
  volatileStateFields: ["lastChangedAt"]
62651
63070
  };
62652
63071
  /**
63072
+ * A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
63073
+ * so dashboards keep one series; every other instantaneous reading lands here.
63074
+ * `value` is the time integral in the source quantity's accumulated base
63075
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
63076
+ */
63077
+ var MeterStatusSchema = object({
63078
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
63079
+ lastFetchedAt: number()
63080
+ });
63081
+ var meterCapability = {
63082
+ name: "meter",
63083
+ scope: "device",
63084
+ deviceNative: true,
63085
+ mode: "singleton",
63086
+ deviceTypes: [DeviceType.Sensor],
63087
+ methods: {},
63088
+ status: {
63089
+ schema: MeterStatusSchema,
63090
+ kind: "push"
63091
+ },
63092
+ runtimeState: MeterStatusSchema,
63093
+ /**
63094
+ * Runtime-state durability: **restored** — the total is a cache of the
63095
+ * history integral. A restart shows it only until the history pass
63096
+ * recomputes the open period; it is not an in-memory buffer.
63097
+ *
63098
+ * See `RuntimeStateDurability`. Enforced by
63099
+ * `scripts/check-runtime-state-durability.ts`.
63100
+ */
63101
+ durability: "restored",
63102
+ history: { fields: { value: {
63103
+ kind: "counter",
63104
+ deadband: .001,
63105
+ minIntervalMs: 6e4
63106
+ } } },
63107
+ volatileStateFields: ["lastFetchedAt"]
63108
+ };
63109
+ /**
62653
63110
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
62654
63111
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
62655
63112
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -65227,9 +65684,9 @@ var PowerMeterStatusSchema = object({
65227
65684
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
65228
65685
  kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
65229
65686
  /** Voltage in volts. */
65230
- volts: number().optional().meta(readingMeta("voltage")),
65687
+ volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
65231
65688
  /** Current in amperes. */
65232
- amps: number().optional().meta(readingMeta("current")),
65689
+ amps: number().optional().meta(readingMeta("current", { label: "Current" })),
65233
65690
  /** Apparent power in volt-amperes. */
65234
65691
  va: number().optional().meta(readingMeta("apparent-power")),
65235
65692
  /** Power factor (0..1) if reported. */
@@ -65237,12 +65694,74 @@ var PowerMeterStatusSchema = object({
65237
65694
  /** Ms epoch when the slice was last updated. */
65238
65695
  lastFetchedAt: number()
65239
65696
  });
65697
+ /**
65698
+ * Loads that draw metered mains power (D951). The reading is a feature of the
65699
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
65700
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
65701
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
65702
+ * camera and the like are left out: their draw is not what anyone meters.
65703
+ */
65704
+ var POWER_METER_DEVICE_TYPES = [
65705
+ DeviceType.Sensor,
65706
+ DeviceType.Switch,
65707
+ DeviceType.Light,
65708
+ DeviceType.Fan,
65709
+ DeviceType.Climate,
65710
+ DeviceType.Thermostat,
65711
+ DeviceType.Humidifier,
65712
+ DeviceType.WaterHeater,
65713
+ DeviceType.MediaPlayer,
65714
+ DeviceType.Washer,
65715
+ DeviceType.Dryer,
65716
+ DeviceType.Dishwasher,
65717
+ DeviceType.Vacuum,
65718
+ DeviceType.LawnMower,
65719
+ DeviceType.PetFeeder
65720
+ ];
65721
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
65722
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
65723
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
65724
+ function powerTarget(id, unit) {
65725
+ return {
65726
+ id,
65727
+ cardinality: "scalar",
65728
+ vocabulary: {
65729
+ cap: "power-meter",
65730
+ stateField: id
65731
+ },
65732
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
65733
+ apply: "add",
65734
+ path: id,
65735
+ sources: [{
65736
+ kind: "numeric",
65737
+ requiredUnit: unit,
65738
+ transforms: [{ kind: "identity" }]
65739
+ }],
65740
+ companions: [{
65741
+ kind: "clock",
65742
+ path: "lastFetchedAt",
65743
+ when: "always"
65744
+ }],
65745
+ sourceReport: { status: "assignment-status" },
65746
+ advancedExpression: true
65747
+ };
65748
+ }
65749
+ /** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
65750
+ var powerMeterCustomize = {
65751
+ version: 1,
65752
+ targets: [
65753
+ powerTarget("watts", "W"),
65754
+ powerTarget("kwhTotal", "kWh"),
65755
+ powerTarget("volts", "V"),
65756
+ powerTarget("amps", "A")
65757
+ ]
65758
+ };
65240
65759
  var powerMeterCapability = {
65241
65760
  name: "power-meter",
65242
65761
  scope: "device",
65243
65762
  deviceNative: true,
65244
65763
  mode: "singleton",
65245
- deviceTypes: [DeviceType.Sensor],
65764
+ deviceTypes: POWER_METER_DEVICE_TYPES,
65246
65765
  methods: {},
65247
65766
  status: {
65248
65767
  schema: PowerMeterStatusSchema,
@@ -65299,7 +65818,8 @@ var powerMeterCapability = {
65299
65818
  } },
65300
65819
  /** Clock fields: written, but excluded from the compare that decides
65301
65820
  * whether persisting is worth a SQLite commit. */
65302
- volatileStateFields: ["lastFetchedAt"]
65821
+ volatileStateFields: ["lastFetchedAt"],
65822
+ customize: powerMeterCustomize
65303
65823
  };
65304
65824
  /**
65305
65825
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -66092,6 +66612,8 @@ var runningCustomize = {
66092
66612
  stateField: "running"
66093
66613
  },
66094
66614
  deviceTypes: [
66615
+ DeviceType.Washer,
66616
+ DeviceType.Dryer,
66095
66617
  DeviceType.Dishwasher,
66096
66618
  DeviceType.Vacuum,
66097
66619
  DeviceType.LawnMower,
@@ -68973,7 +69495,8 @@ var switchCapability = {
68973
69495
  deviceTypes: [
68974
69496
  DeviceType.Switch,
68975
69497
  DeviceType.Siren,
68976
- DeviceType.Light
69498
+ DeviceType.Light,
69499
+ DeviceType.Fan
68977
69500
  ],
68978
69501
  methods: { setState: method(object({
68979
69502
  deviceId: number(),
@@ -71319,8 +71842,9 @@ function withClipEventLabels(provider, deps) {
71319
71842
  listClips
71320
71843
  };
71321
71844
  }
71845
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
71322
71846
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
71323
- 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;
71847
+ 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;
71324
71848
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
71325
71849
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
71326
71850
  new Set(CAP_NAMES);
@@ -71664,6 +72188,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
71664
72188
  featureProbe: featureProbeCapability,
71665
72189
  flood: floodCapability,
71666
72190
  gas: gasCapability,
72191
+ groupActive: groupActiveCapability,
71667
72192
  groupCommands: groupCommandsCapability,
71668
72193
  groupStatus: groupStatusCapability,
71669
72194
  heatingValve: heatingValveCapability,
@@ -71675,6 +72200,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
71675
72200
  lawnMowerControl: lawnMowerControlCapability,
71676
72201
  lockControl: lockControlCapability,
71677
72202
  mediaPlayer: mediaPlayerCapability,
72203
+ meter: meterCapability,
71678
72204
  motion: motionCapability,
71679
72205
  motionTrigger: motionTriggerCapability,
71680
72206
  motionZones: motionZonesCapability,
@@ -72934,7 +73460,7 @@ var BaseDeviceProvider = class extends BaseAddon {
72934
73460
  return toDeviceSummary(device, this.addonId);
72935
73461
  }
72936
73462
  };
72937
- 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;
73463
+ 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;
72938
73464
  new Set(Object.values(DeviceType));
72939
73465
  Object.freeze({
72940
73466
  "accessories.getStatus": {
@@ -76157,6 +76683,13 @@ Object.freeze({
76157
76683
  access: "view",
76158
76684
  role: "viewer"
76159
76685
  },
76686
+ "groupActive.getStatus": {
76687
+ capName: "group-active",
76688
+ capScope: "device",
76689
+ addonId: null,
76690
+ access: "view",
76691
+ role: "viewer"
76692
+ },
76160
76693
  "groupCommands.getStatus": {
76161
76694
  capName: "group-commands",
76162
76695
  capScope: "device",
@@ -77123,6 +77656,13 @@ Object.freeze({
77123
77656
  access: "create",
77124
77657
  role: "admin"
77125
77658
  },
77659
+ "meter.getStatus": {
77660
+ capName: "meter",
77661
+ capScope: "device",
77662
+ addonId: null,
77663
+ access: "view",
77664
+ role: "viewer"
77665
+ },
77126
77666
  "metricsProvider.collectSnapshot": {
77127
77667
  capName: "metrics-provider",
77128
77668
  capScope: "system",
@@ -85221,7 +85761,39 @@ var APPLIANCE_CAP_VOCABULARY = {
85221
85761
  phrase: t("When {device} finishes", "Quando {device} finisce"),
85222
85762
  icon: "square"
85223
85763
  }
85224
- }
85764
+ },
85765
+ overrides: { byType: {
85766
+ [DeviceType.Washer]: {
85767
+ state: { running: {
85768
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
85769
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
85770
+ } },
85771
+ events: {
85772
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
85773
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
85774
+ }
85775
+ },
85776
+ [DeviceType.Dryer]: {
85777
+ state: { running: {
85778
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
85779
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
85780
+ } },
85781
+ events: {
85782
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
85783
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
85784
+ }
85785
+ },
85786
+ [DeviceType.Dishwasher]: {
85787
+ state: { running: {
85788
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
85789
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
85790
+ } },
85791
+ events: {
85792
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
85793
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
85794
+ }
85795
+ }
85796
+ } }
85225
85797
  },
85226
85798
  update: {
85227
85799
  scope: "device",
@@ -85993,6 +86565,10 @@ var SENSOR_CAP_VOCABULARY = {
85993
86565
  icon: "flame"
85994
86566
  }]
85995
86567
  },
86568
+ "group-active": {
86569
+ scope: "device",
86570
+ noun: t("Group state", "Stato del gruppo")
86571
+ },
85996
86572
  "group-commands": {
85997
86573
  scope: "device",
85998
86574
  noun: t("Group command", "Comando di gruppo")
@@ -86049,6 +86625,17 @@ var SENSOR_CAP_VOCABULARY = {
86049
86625
  }],
86050
86626
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
86051
86627
  },
86628
+ meter: {
86629
+ scope: "device",
86630
+ noun: t("Meter", "Contatore"),
86631
+ state: [{
86632
+ kind: "numeric",
86633
+ field: "value",
86634
+ quantity: t("the total", "il totale"),
86635
+ label: t("Total", "Totale"),
86636
+ icon: "gauge"
86637
+ }]
86638
+ },
86052
86639
  "numeric-sensor": {
86053
86640
  scope: "device",
86054
86641
  noun: t("Sensor", "Sensore"),
@@ -86521,8 +87108,47 @@ function projectAction(cap, action) {
86521
87108
  };
86522
87109
  }
86523
87110
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
86524
- 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;
87111
+ 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;
86525
87112
  DeviceFeature.BatteryOperated;
87113
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
87114
+ 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.";
87115
+ 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.";
87116
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
87117
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
87118
+ 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.";
87119
+ function withheld(reason) {
87120
+ return {
87121
+ [DeviceType.Washer]: {
87122
+ exported: false,
87123
+ reason
87124
+ },
87125
+ [DeviceType.Dryer]: {
87126
+ exported: false,
87127
+ reason
87128
+ },
87129
+ [DeviceType.Dishwasher]: {
87130
+ exported: false,
87131
+ reason
87132
+ }
87133
+ };
87134
+ }
87135
+ function offered(received) {
87136
+ return {
87137
+ [DeviceType.Washer]: {
87138
+ exported: true,
87139
+ as: received
87140
+ },
87141
+ [DeviceType.Dryer]: {
87142
+ exported: true,
87143
+ as: received
87144
+ },
87145
+ [DeviceType.Dishwasher]: {
87146
+ exported: true,
87147
+ as: received
87148
+ }
87149
+ };
87150
+ }
87151
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
86526
87152
  var LAST_FETCHED_FIELD = "lastFetchedAt";
86527
87153
  /**
86528
87154
  * How long a bridge stops re-attempting a refresh that did not land.
@@ -87641,6 +88267,11 @@ Object.freeze({
87641
88267
  form: "single",
87642
88268
  optional: false
87643
88269
  }],
88270
+ "groupActive.getStatus": [{
88271
+ name: "deviceId",
88272
+ form: "single",
88273
+ optional: false
88274
+ }],
87644
88275
  "groupCommands.getStatus": [{
87645
88276
  name: "deviceId",
87646
88277
  form: "single",
@@ -87851,6 +88482,11 @@ Object.freeze({
87851
88482
  form: "single",
87852
88483
  optional: false
87853
88484
  }],
88485
+ "meter.getStatus": [{
88486
+ name: "deviceId",
88487
+ form: "single",
88488
+ optional: false
88489
+ }],
87854
88490
  "motion.getStatus": [{
87855
88491
  name: "deviceId",
87856
88492
  form: "single",
@@ -89458,7 +90094,6 @@ Object.freeze({
89458
90094
  "network-access": "ingress",
89459
90095
  "smtp-provider": "email"
89460
90096
  });
89461
- DeviceType.Camera;
89462
90097
  /**
89463
90098
  * A saved Customize assignment (D936). The schema is the type: callers use
89464
90099
  * `z.infer`, they do not restate the shape.