@camstack/addon-provider-onvif 1.2.149 → 1.2.150

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
@@ -5446,7 +5446,7 @@ var ZodIssueCode = {
5446
5446
  var ZodFirstPartyTypeKind;
5447
5447
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5448
5448
  //#endregion
5449
- //#region ../types/dist/sleep-bykKrTgu.mjs
5449
+ //#region ../types/dist/sleep--jXS9hWo.mjs
5450
5450
  /**
5451
5451
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5452
5452
  * window to float samples (D455).
@@ -8568,6 +8568,7 @@ var CAP_NAMES = Object.freeze([
8568
8568
  "filesystem-browse",
8569
8569
  "flood",
8570
8570
  "gas",
8571
+ "group-active",
8571
8572
  "group-commands",
8572
8573
  "group-status",
8573
8574
  "heating-valve",
@@ -8590,6 +8591,7 @@ var CAP_NAMES = Object.freeze([
8590
8591
  "login-method",
8591
8592
  "media-player",
8592
8593
  "mesh-network",
8594
+ "meter",
8593
8595
  "metrics-provider",
8594
8596
  "model-convert",
8595
8597
  "model-distributor",
@@ -9388,6 +9390,10 @@ Object.freeze({
9388
9390
  "path": "detected",
9389
9391
  "valueType": "boolean"
9390
9392
  }],
9393
+ "group-active": [{
9394
+ "path": "active",
9395
+ "valueType": "boolean"
9396
+ }],
9391
9397
  "group-status": [
9392
9398
  {
9393
9399
  "path": "included",
@@ -9647,6 +9653,10 @@ Object.freeze({
9647
9653
  ]
9648
9654
  }
9649
9655
  ],
9656
+ "meter": [{
9657
+ "path": "value",
9658
+ "valueType": "number"
9659
+ }],
9650
9660
  "motion": [
9651
9661
  {
9652
9662
  "path": "detected",
@@ -10390,7 +10400,7 @@ object({
10390
10400
  params: record(string(), union([string(), number()])).optional()
10391
10401
  });
10392
10402
  //#endregion
10393
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10403
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10394
10404
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10395
10405
  DeviceType["Camera"] = "camera";
10396
10406
  DeviceType["Hub"] = "hub";
@@ -10484,8 +10494,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10484
10494
  DeviceType["PetFeeder"] = "pet-feeder";
10485
10495
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10486
10496
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10487
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10497
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10498
+ * A plug-built dishwasher carries running-state and power instead. */
10488
10499
  DeviceType["Dishwasher"] = "dishwasher";
10500
+ /** Washing machine. A plug-built one is running while its power stays
10501
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10502
+ DeviceType["Washer"] = "washer";
10503
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10504
+ DeviceType["Dryer"] = "dryer";
10489
10505
  return DeviceType;
10490
10506
  }({});
10491
10507
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -10743,7 +10759,7 @@ function errMsg(err) {
10743
10759
  return String(err);
10744
10760
  }
10745
10761
  //#endregion
10746
- //#region ../types/dist/composition-OhpuespA.mjs
10762
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
10747
10763
  /**
10748
10764
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10749
10765
  *
@@ -11058,6 +11074,13 @@ var table = {
11058
11074
  apply: () => {
11059
11075
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11060
11076
  }
11077
+ },
11078
+ schmitt: {
11079
+ minArgs: 3,
11080
+ maxArgs: 5,
11081
+ apply: () => {
11082
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11083
+ }
11061
11084
  }
11062
11085
  };
11063
11086
  Object.freeze(Object.assign(Object.create(null), table));
@@ -11499,6 +11522,99 @@ function validateExpressionSource(src) {
11499
11522
  return null;
11500
11523
  }
11501
11524
  /**
11525
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11526
+ *
11527
+ * Standby of the control board is under 5 W; a motor or a heater is far
11528
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11529
+ * a cycle, so running starts only after 30 s above the line.
11530
+ *
11531
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11532
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11533
+ * report "finished" in the middle of the program, so the stop dwell is
11534
+ * 10 minutes on every type. The number is stored per type so one machine
11535
+ * can move later without moving the others.
11536
+ */
11537
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11538
+ var APPLIANCE_KINDS = [
11539
+ "washer",
11540
+ "dryer",
11541
+ "dishwasher"
11542
+ ];
11543
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11544
+ /**
11545
+ * Statistics and integrals over a history read (D944).
11546
+ *
11547
+ * Pure: the same points always give the same answer, so a restart recomputes
11548
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11549
+ * never the mean of the samples. A hole with no held value is not integrated
11550
+ * as zero. A truncated read is not published.
11551
+ */
11552
+ var STATISTIC_STATS = [
11553
+ "min",
11554
+ "max",
11555
+ "mean",
11556
+ "sum",
11557
+ "count",
11558
+ "change"
11559
+ ];
11560
+ /**
11561
+ * The range a statistics or integral helper asks history for (D944).
11562
+ *
11563
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11564
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11565
+ * is strictly longer than the longest that range can be. Forever qualifies.
11566
+ * Equal to a tier's retention is a refusal, not a short answer.
11567
+ */
11568
+ var MINUTE_MS = 6e4;
11569
+ var HOUR_MS = 60 * MINUTE_MS;
11570
+ var DAY_MS = 24 * HOUR_MS;
11571
+ var ROLLING_SPANS = [
11572
+ "1h",
11573
+ "24h",
11574
+ "2d",
11575
+ "7d",
11576
+ "30d",
11577
+ "custom"
11578
+ ];
11579
+ var CALENDAR_PERIODS = [
11580
+ "today",
11581
+ "yesterday",
11582
+ "this-week",
11583
+ "this-month"
11584
+ ];
11585
+ var RESET_PERIODS = [
11586
+ "never",
11587
+ "daily",
11588
+ "weekly",
11589
+ "monthly"
11590
+ ];
11591
+ 5 * MINUTE_MS;
11592
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
11593
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
11594
+ var rollingWindowObject = object({
11595
+ kind: literal("rolling"),
11596
+ span: _enum(ROLLING_SPANS),
11597
+ customMinutes: number().int().min(5).optional()
11598
+ }).check((payload) => {
11599
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
11600
+ code: "custom",
11601
+ message: "a custom range needs a duration in minutes",
11602
+ path: ["customMinutes"],
11603
+ input: payload.value
11604
+ });
11605
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
11606
+ code: "custom",
11607
+ message: "only a custom range carries a duration",
11608
+ path: ["customMinutes"],
11609
+ input: payload.value
11610
+ });
11611
+ });
11612
+ var RollingWindowSchema = rollingWindowObject;
11613
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
11614
+ kind: literal("calendar"),
11615
+ period: _enum(CALENDAR_PERIODS)
11616
+ })]);
11617
+ /**
11502
11618
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11503
11619
  * id. Split out so helper schemas can name it without a cycle through
11504
11620
  * `composition.ts` (D659).
@@ -11524,7 +11640,13 @@ var GROUP_TEMPLATE_IDS = [
11524
11640
  "occupancy",
11525
11641
  "binary",
11526
11642
  "switch",
11527
- "light"
11643
+ "light",
11644
+ "fan",
11645
+ "cover",
11646
+ "valve",
11647
+ "lock",
11648
+ "media-player",
11649
+ "climate"
11528
11650
  ];
11529
11651
  var NUMERIC_AGGREGATES = [
11530
11652
  "mean",
@@ -11554,14 +11676,73 @@ var HelperMemberSchema = object({
11554
11676
  cap: string().min(1),
11555
11677
  fieldPath: string().min(1)
11556
11678
  });
11557
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11558
- kind: literal("group"),
11559
- templateId: GroupTemplateIdSchema,
11560
- aggregate: AggregateReducerSchema,
11561
- members: array(HelperMemberSchema).min(1).max(64),
11562
- /** Actuator groups only. Readings leave this unset. */
11563
- active: ActiveCriterionSchema.optional()
11564
- })]);
11679
+ var HelperSpecSchema = discriminatedUnion("kind", [
11680
+ object({
11681
+ kind: literal("group"),
11682
+ templateId: GroupTemplateIdSchema,
11683
+ aggregate: AggregateReducerSchema,
11684
+ members: array(HelperMemberSchema).min(1).max(64),
11685
+ /** Actuator groups only. Readings leave this unset. */
11686
+ active: ActiveCriterionSchema.optional()
11687
+ }),
11688
+ object({
11689
+ kind: literal("appliance"),
11690
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
11691
+ appliance: _enum(APPLIANCE_KINDS),
11692
+ thresholdWatts: number().positive(),
11693
+ startDwellMs: number().int().nonnegative(),
11694
+ stopDwellMs: number().int().nonnegative(),
11695
+ member: HelperMemberSchema
11696
+ }),
11697
+ object({
11698
+ kind: literal("statistic"),
11699
+ stat: _enum(STATISTIC_STATS),
11700
+ window: StatisticWindowSchema,
11701
+ source: HelperMemberSchema
11702
+ }),
11703
+ object({
11704
+ kind: literal("integral"),
11705
+ reset: _enum(RESET_PERIODS),
11706
+ output: _enum(["energy", "meter"]),
11707
+ source: HelperMemberSchema
11708
+ }),
11709
+ object({
11710
+ kind: literal("threshold"),
11711
+ source: HelperMemberSchema,
11712
+ high: number(),
11713
+ low: number(),
11714
+ onForMs: number().int().nonnegative(),
11715
+ offForMs: number().int().nonnegative()
11716
+ }),
11717
+ object({
11718
+ kind: literal("arithmetic"),
11719
+ op: _enum([
11720
+ "sum",
11721
+ "difference",
11722
+ "ratio"
11723
+ ]),
11724
+ inputs: array(HelperMemberSchema).min(2).max(64)
11725
+ }),
11726
+ object({
11727
+ kind: literal("expression"),
11728
+ expr: string().min(1),
11729
+ inputs: array(object({
11730
+ name: string().min(1),
11731
+ member: HelperMemberSchema
11732
+ })).min(1).max(64),
11733
+ output: _enum(["number", "boolean"])
11734
+ }),
11735
+ object({
11736
+ kind: literal("derivative"),
11737
+ window: RollingWindowSchema,
11738
+ per: _enum([
11739
+ "second",
11740
+ "minute",
11741
+ "hour"
11742
+ ]),
11743
+ source: HelperMemberSchema
11744
+ })
11745
+ ]);
11565
11746
  /** A composed device's stableId is this prefix plus the block id. */
11566
11747
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11567
11748
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -11622,6 +11803,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11622
11803
  "all"
11623
11804
  ]),
11624
11805
  reads: array(CompositionFieldReadSchema).min(1).max(64),
11806
+ /**
11807
+ * Set when the reads are an enum state, not a yes/no: the listed states
11808
+ * count as active (a cover is `open`). A state outside the list is inactive;
11809
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
11810
+ */
11811
+ activeWhen: array(string().min(1)).min(1).optional(),
11625
11812
  /** `value` is the reading; the others are the member counts. */
11626
11813
  publish: _enum([
11627
11814
  "value",
@@ -11635,6 +11822,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11635
11822
  "omit",
11636
11823
  "offline"
11637
11824
  ])
11825
+ }),
11826
+ object({
11827
+ kind: literal("series"),
11828
+ read: CompositionFieldReadSchema,
11829
+ /** `value` is the statistic or the integral. The others are the gap counts. */
11830
+ publish: _enum([
11831
+ "value",
11832
+ "included",
11833
+ "excluded",
11834
+ "total"
11835
+ ]),
11836
+ /**
11837
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
11838
+ * device unseeded until history answers, so a restart cannot publish 0.
11839
+ */
11840
+ empty: _enum([
11841
+ "null",
11842
+ "omit",
11843
+ "offline"
11844
+ ])
11638
11845
  })
11639
11846
  ]);
11640
11847
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -16794,7 +17001,22 @@ var BrokerProviderInfoSchema = object({
16794
17001
  */
16795
17002
  interactiveConnectPath: string().min(1).optional(),
16796
17003
  /** Relative hub path to disconnect the linked account (same popup pattern). */
16797
- interactiveDisconnectPath: string().min(1).optional()
17004
+ interactiveDisconnectPath: string().min(1).optional(),
17005
+ /**
17006
+ * The kind signs in through a server-held interactive session instead of
17007
+ * (or before) typed credentials. `basePath` is a hub-relative route set
17008
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
17009
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
17010
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
17011
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
17012
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
17013
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
17014
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
17015
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
17016
+ * offers it as the default path; the fields the session fills are
17017
+ * marked `filledBySignIn` in the kind's settings schema.
17018
+ */
17019
+ signIn: object({ basePath: string().min(1) }).optional()
16798
17020
  }))
16799
17021
  });
16800
17022
  var ListInputSchema = object({
@@ -17793,7 +18015,8 @@ var CompositionProblemSchema = object({
17793
18015
  "native-capability-replace",
17794
18016
  "mixed-quantity",
17795
18017
  "helper-mismatch",
17796
- "member-constraint"
18018
+ "member-constraint",
18019
+ "retention"
17797
18020
  ]),
17798
18021
  path: string(),
17799
18022
  message: string()
@@ -18027,13 +18250,17 @@ Object.freeze({
18027
18250
  latchedAt: {
18028
18251
  kind: "fixed",
18029
18252
  result: ["number", "null"]
18253
+ },
18254
+ schmitt: {
18255
+ kind: "fixed",
18256
+ result: ["boolean", "null"]
18030
18257
  }
18031
18258
  });
18032
18259
  /**
18033
18260
  * One catalog row per base reading a group helper can build (D944 §3).
18034
18261
  * Data, not a form default written into the block.
18035
18262
  */
18036
- var NOW = {
18263
+ var NOW$2 = {
18037
18264
  fieldPath: "lastFetchedAt",
18038
18265
  expr: "now"
18039
18266
  };
@@ -18047,6 +18274,8 @@ function numeric(input) {
18047
18274
  family: "numeric",
18048
18275
  deviceType: DeviceType.Sensor,
18049
18276
  memberDeviceType: DeviceType.Sensor,
18277
+ memberDeviceTypes: [DeviceType.Sensor],
18278
+ activeWhen: void 0,
18050
18279
  commandCaps: [],
18051
18280
  baseUnit: baseUnitOf(input.quantity),
18052
18281
  aggregates: NUMERIC_AGGREGATES,
@@ -18062,6 +18291,8 @@ function binary(input) {
18062
18291
  family: "binary",
18063
18292
  deviceType: DeviceType.Sensor,
18064
18293
  memberDeviceType: DeviceType.Sensor,
18294
+ memberDeviceTypes: [DeviceType.Sensor],
18295
+ activeWhen: void 0,
18065
18296
  commandCaps: [],
18066
18297
  quantity: "none",
18067
18298
  baseUnit: void 0,
@@ -18079,7 +18310,7 @@ var GROUP_TEMPLATES = [
18079
18310
  quantity: "temperature",
18080
18311
  defaultAggregate: "mean",
18081
18312
  empty: "null",
18082
- companions: [NOW]
18313
+ companions: [NOW$2]
18083
18314
  }),
18084
18315
  numeric({
18085
18316
  id: "humidity",
@@ -18090,7 +18321,7 @@ var GROUP_TEMPLATES = [
18090
18321
  quantity: "humidity",
18091
18322
  defaultAggregate: "mean",
18092
18323
  empty: "null",
18093
- companions: [NOW]
18324
+ companions: [NOW$2]
18094
18325
  }),
18095
18326
  numeric({
18096
18327
  id: "pressure",
@@ -18101,7 +18332,7 @@ var GROUP_TEMPLATES = [
18101
18332
  quantity: "pressure",
18102
18333
  defaultAggregate: "mean",
18103
18334
  empty: "null",
18104
- companions: [NOW]
18335
+ companions: [NOW$2]
18105
18336
  }),
18106
18337
  numeric({
18107
18338
  id: "illuminance",
@@ -18112,7 +18343,7 @@ var GROUP_TEMPLATES = [
18112
18343
  quantity: "illuminance",
18113
18344
  defaultAggregate: "mean",
18114
18345
  empty: "null",
18115
- companions: [NOW]
18346
+ companions: [NOW$2]
18116
18347
  }),
18117
18348
  numeric({
18118
18349
  id: "power",
@@ -18123,7 +18354,7 @@ var GROUP_TEMPLATES = [
18123
18354
  quantity: "power",
18124
18355
  defaultAggregate: "sum",
18125
18356
  empty: "omit",
18126
- companions: [NOW]
18357
+ companions: [NOW$2]
18127
18358
  }),
18128
18359
  numeric({
18129
18360
  id: "energy",
@@ -18134,7 +18365,7 @@ var GROUP_TEMPLATES = [
18134
18365
  quantity: "energy",
18135
18366
  defaultAggregate: "sum",
18136
18367
  empty: "omit",
18137
- companions: [NOW]
18368
+ companions: [NOW$2]
18138
18369
  }),
18139
18370
  numeric({
18140
18371
  id: "battery",
@@ -18169,7 +18400,7 @@ var GROUP_TEMPLATES = [
18169
18400
  quantity: "device",
18170
18401
  defaultAggregate: "mean",
18171
18402
  empty: "null",
18172
- companions: [NOW]
18403
+ companions: [NOW$2]
18173
18404
  }),
18174
18405
  binary({
18175
18406
  id: "motion",
@@ -18263,6 +18494,11 @@ var GROUP_TEMPLATES = [
18263
18494
  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.",
18264
18495
  deviceType: DeviceType.Switch,
18265
18496
  outputCap: "switch",
18497
+ outputField: "on",
18498
+ member: {
18499
+ cap: "switch",
18500
+ fieldPath: "on"
18501
+ },
18266
18502
  commandCaps: ["switch"]
18267
18503
  }),
18268
18504
  actuator({
@@ -18270,34 +18506,136 @@ var GROUP_TEMPLATES = [
18270
18506
  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.",
18271
18507
  deviceType: DeviceType.Light,
18272
18508
  outputCap: "switch",
18509
+ outputField: "on",
18510
+ member: {
18511
+ cap: "switch",
18512
+ fieldPath: "on"
18513
+ },
18273
18514
  commandCaps: [
18274
18515
  "switch",
18275
18516
  "brightness",
18276
18517
  "color"
18277
18518
  ]
18519
+ }),
18520
+ actuator({
18521
+ id: "fan",
18522
+ 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.",
18523
+ deviceType: DeviceType.Fan,
18524
+ outputCap: "switch",
18525
+ outputField: "on",
18526
+ member: {
18527
+ cap: "switch",
18528
+ fieldPath: "on"
18529
+ },
18530
+ commandCaps: ["switch", "fan-control"]
18531
+ }),
18532
+ actuator({
18533
+ id: "cover",
18534
+ 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.",
18535
+ deviceType: DeviceType.Cover,
18536
+ outputCap: "group-active",
18537
+ outputField: "active",
18538
+ member: {
18539
+ cap: "cover",
18540
+ fieldPath: "state"
18541
+ },
18542
+ activeWhen: [
18543
+ "open",
18544
+ "opening",
18545
+ "stopped"
18546
+ ],
18547
+ commandCaps: ["cover"]
18548
+ }),
18549
+ actuator({
18550
+ id: "valve",
18551
+ 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.",
18552
+ deviceType: DeviceType.Valve,
18553
+ outputCap: "group-active",
18554
+ outputField: "active",
18555
+ member: {
18556
+ cap: "valve",
18557
+ fieldPath: "state"
18558
+ },
18559
+ activeWhen: [
18560
+ "open",
18561
+ "opening",
18562
+ "stopped"
18563
+ ],
18564
+ commandCaps: ["valve"]
18565
+ }),
18566
+ actuator({
18567
+ id: "lock",
18568
+ 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.",
18569
+ deviceType: DeviceType.Lock,
18570
+ outputCap: "group-active",
18571
+ outputField: "active",
18572
+ member: {
18573
+ cap: "lock-control",
18574
+ fieldPath: "state"
18575
+ },
18576
+ activeWhen: ["locked"],
18577
+ commandCaps: ["lock-control"]
18578
+ }),
18579
+ actuator({
18580
+ id: "media-player",
18581
+ 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.",
18582
+ deviceType: DeviceType.MediaPlayer,
18583
+ outputCap: "group-active",
18584
+ outputField: "active",
18585
+ member: {
18586
+ cap: "media-player",
18587
+ fieldPath: "state"
18588
+ },
18589
+ activeWhen: ["playing", "buffering"],
18590
+ commandCaps: ["media-player"]
18591
+ }),
18592
+ actuator({
18593
+ id: "climate",
18594
+ 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.",
18595
+ deviceType: DeviceType.Thermostat,
18596
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
18597
+ outputCap: "group-active",
18598
+ outputField: "active",
18599
+ member: {
18600
+ cap: "climate-control",
18601
+ fieldPath: "mode"
18602
+ },
18603
+ activeWhen: [
18604
+ "heat",
18605
+ "cool",
18606
+ "auto",
18607
+ "heat_cool",
18608
+ "fan_only",
18609
+ "dry"
18610
+ ],
18611
+ commandCaps: ["climate-control"]
18278
18612
  })
18279
18613
  ];
18280
18614
  function actuator(input) {
18281
18615
  return {
18282
- ...input,
18616
+ id: input.id,
18617
+ summary: input.summary,
18618
+ deviceType: input.deviceType,
18619
+ outputCap: input.outputCap,
18620
+ outputField: input.outputField,
18621
+ commandCaps: input.commandCaps,
18283
18622
  family: "actuator",
18284
18623
  role: void 0,
18285
18624
  memberDeviceType: input.deviceType,
18286
- outputField: "on",
18625
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
18626
+ activeWhen: input.activeWhen,
18287
18627
  quantity: "none",
18288
18628
  baseUnit: void 0,
18289
18629
  aggregates: BINARY_AGGREGATES,
18290
18630
  defaultAggregate: "any",
18291
18631
  empty: "offline",
18292
- accept: [{
18293
- cap: "switch",
18294
- fieldPath: "on"
18295
- }],
18632
+ accept: [input.member],
18296
18633
  companions: [CHANGED]
18297
18634
  };
18298
18635
  }
18299
18636
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
18300
18637
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
18638
+ DeviceType.Camera;
18301
18639
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
18302
18640
  /**
18303
18641
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -43506,7 +43844,7 @@ var NetworkAccessStatusSchema = object({
43506
43844
  });
43507
43845
  /**
43508
43846
  * Optional, richer endpoint shape returned by providers that expose
43509
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
43847
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
43510
43848
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
43511
43849
  * the originating provider config (mode + sourcePort) so the
43512
43850
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -46149,7 +46487,20 @@ var NcRuleInputSchema = object({
46149
46487
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
46150
46488
  * path does not run Zod defaults.
46151
46489
  */
46152
- groupingSec: number().int().min(5).max(60).optional()
46490
+ groupingSec: number().int().min(5).max(60).optional(),
46491
+ /**
46492
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
46493
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
46494
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
46495
+ * same mark the Library writes, and records whose notification it was and
46496
+ * when. A channel with no buttons gets the same two choices as links.
46497
+ *
46498
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
46499
+ * cap path): absent and `false` both mean off, which is what every rule
46500
+ * authored before this field meant. An editor turns it off by writing
46501
+ * `false`, so it needs no `clear` key.
46502
+ */
46503
+ askVerdict: boolean().optional()
46153
46504
  });
46154
46505
  /**
46155
46506
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -56710,7 +57061,7 @@ var BatteryStatusSchema = object({
56710
57061
  */
56711
57062
  binary: boolean().optional().meta({ label: "Binary indicator" })
56712
57063
  });
56713
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat;
57064
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter;
56714
57065
  method(object({
56715
57066
  deviceId: number(),
56716
57067
  /** Bound on the wait. Sensible range 3000–10000ms. */
@@ -58454,7 +58805,13 @@ results: array(object({
58454
58805
  /** Ms epoch of the attempt. */
58455
58806
  at: number()
58456
58807
  })) });
58457
- DeviceType.Switch, DeviceType.Light;
58808
+ DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
58809
+ object({
58810
+ active: boolean().meta({ label: "Active" }),
58811
+ /** Ms epoch when the slice was last updated. */
58812
+ lastChangedAt: number()
58813
+ });
58814
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
58458
58815
  object({
58459
58816
  /** Members whose current value entered the aggregate. */
58460
58817
  included: number().int().nonnegative().meta(readingMeta("count", {
@@ -58472,7 +58829,7 @@ object({
58472
58829
  label: "Members"
58473
58830
  }))
58474
58831
  });
58475
- DeviceType.Sensor, DeviceType.Switch, DeviceType.Light;
58832
+ DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
58476
58833
  object({
58477
58834
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
58478
58835
  position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -59379,7 +59736,14 @@ DeviceType.Lock, method(object({
59379
59736
  auth: "protected"
59380
59737
  }), method(object({
59381
59738
  deviceId: number().int().nonnegative(),
59382
- code: string().min(1).optional()
59739
+ code: string().min(1).optional(),
59740
+ /**
59741
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
59742
+ * with one call is refused unless the caller says it was confirmed. A
59743
+ * single lock ignores it. The refusal lives in the callee, so no
59744
+ * automation or API caller reaches the doors by skipping a dialog.
59745
+ */
59746
+ confirm: boolean().optional()
59383
59747
  }), _void(), {
59384
59748
  kind: "mutation",
59385
59749
  auth: "protected"
@@ -59387,6 +59751,11 @@ DeviceType.Lock, method(object({
59387
59751
  kind: "mutation",
59388
59752
  auth: "protected"
59389
59753
  });
59754
+ object({
59755
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
59756
+ lastFetchedAt: number()
59757
+ });
59758
+ DeviceType.Sensor;
59390
59759
  /**
59391
59760
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
59392
59761
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
@@ -61294,9 +61663,9 @@ object({
61294
61663
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
61295
61664
  kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
61296
61665
  /** Voltage in volts. */
61297
- volts: number().optional().meta(readingMeta("voltage")),
61666
+ volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
61298
61667
  /** Current in amperes. */
61299
- amps: number().optional().meta(readingMeta("current")),
61668
+ amps: number().optional().meta(readingMeta("current", { label: "Current" })),
61300
61669
  /** Apparent power in volt-amperes. */
61301
61670
  va: number().optional().meta(readingMeta("apparent-power")),
61302
61671
  /** Power factor (0..1) if reported. */
@@ -61304,7 +61673,23 @@ object({
61304
61673
  /** Ms epoch when the slice was last updated. */
61305
61674
  lastFetchedAt: number()
61306
61675
  });
61307
- DeviceType.Sensor;
61676
+ [
61677
+ DeviceType.Sensor,
61678
+ DeviceType.Switch,
61679
+ DeviceType.Light,
61680
+ DeviceType.Fan,
61681
+ DeviceType.Climate,
61682
+ DeviceType.Thermostat,
61683
+ DeviceType.Humidifier,
61684
+ DeviceType.WaterHeater,
61685
+ DeviceType.MediaPlayer,
61686
+ DeviceType.Washer,
61687
+ DeviceType.Dryer,
61688
+ DeviceType.Dishwasher,
61689
+ DeviceType.Vacuum,
61690
+ DeviceType.LawnMower,
61691
+ DeviceType.PetFeeder
61692
+ ].filter((type) => type !== DeviceType.Sensor);
61308
61693
  /**
61309
61694
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
61310
61695
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -61843,7 +62228,7 @@ object({
61843
62228
  phase: string().nullable()
61844
62229
  });
61845
62230
  Object.values(DeviceType);
61846
- DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
62231
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
61847
62232
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
61848
62233
  kind: "mutation",
61849
62234
  auth: "admin"
@@ -63882,7 +64267,7 @@ object({
63882
64267
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
63883
64268
  lastChangedAt: number()
63884
64269
  });
63885
- DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
64270
+ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
63886
64271
  deviceId: number(),
63887
64272
  on: boolean()
63888
64273
  }), _void(), {
@@ -65574,8 +65959,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
65574
65959
  kind: "mutation",
65575
65960
  auth: "admin"
65576
65961
  });
65962
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
65577
65963
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
65578
- 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;
65964
+ 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;
65579
65965
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
65580
65966
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
65581
65967
  new Set(CAP_NAMES);
@@ -66453,7 +66839,7 @@ var BaseDeviceProvider = class extends BaseAddon {
66453
66839
  return toDeviceSummary(device, this.addonId);
66454
66840
  }
66455
66841
  };
66456
- 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;
66842
+ 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;
66457
66843
  new Set(Object.values(DeviceType));
66458
66844
  Object.freeze({
66459
66845
  "accessories.getStatus": {
@@ -69676,6 +70062,13 @@ Object.freeze({
69676
70062
  access: "view",
69677
70063
  role: "viewer"
69678
70064
  },
70065
+ "groupActive.getStatus": {
70066
+ capName: "group-active",
70067
+ capScope: "device",
70068
+ addonId: null,
70069
+ access: "view",
70070
+ role: "viewer"
70071
+ },
69679
70072
  "groupCommands.getStatus": {
69680
70073
  capName: "group-commands",
69681
70074
  capScope: "device",
@@ -70642,6 +71035,13 @@ Object.freeze({
70642
71035
  access: "create",
70643
71036
  role: "admin"
70644
71037
  },
71038
+ "meter.getStatus": {
71039
+ capName: "meter",
71040
+ capScope: "device",
71041
+ addonId: null,
71042
+ access: "view",
71043
+ role: "viewer"
71044
+ },
70645
71045
  "metricsProvider.collectSnapshot": {
70646
71046
  capName: "metrics-provider",
70647
71047
  capScope: "system",
@@ -78740,7 +79140,39 @@ var APPLIANCE_CAP_VOCABULARY = {
78740
79140
  phrase: t("When {device} finishes", "Quando {device} finisce"),
78741
79141
  icon: "square"
78742
79142
  }
78743
- }
79143
+ },
79144
+ overrides: { byType: {
79145
+ [DeviceType.Washer]: {
79146
+ state: { running: {
79147
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
79148
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
79149
+ } },
79150
+ events: {
79151
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
79152
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
79153
+ }
79154
+ },
79155
+ [DeviceType.Dryer]: {
79156
+ state: { running: {
79157
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
79158
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
79159
+ } },
79160
+ events: {
79161
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
79162
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
79163
+ }
79164
+ },
79165
+ [DeviceType.Dishwasher]: {
79166
+ state: { running: {
79167
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
79168
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
79169
+ } },
79170
+ events: {
79171
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
79172
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
79173
+ }
79174
+ }
79175
+ } }
78744
79176
  },
78745
79177
  update: {
78746
79178
  scope: "device",
@@ -79512,6 +79944,10 @@ var SENSOR_CAP_VOCABULARY = {
79512
79944
  icon: "flame"
79513
79945
  }]
79514
79946
  },
79947
+ "group-active": {
79948
+ scope: "device",
79949
+ noun: t("Group state", "Stato del gruppo")
79950
+ },
79515
79951
  "group-commands": {
79516
79952
  scope: "device",
79517
79953
  noun: t("Group command", "Comando di gruppo")
@@ -79568,6 +80004,17 @@ var SENSOR_CAP_VOCABULARY = {
79568
80004
  }],
79569
80005
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
79570
80006
  },
80007
+ meter: {
80008
+ scope: "device",
80009
+ noun: t("Meter", "Contatore"),
80010
+ state: [{
80011
+ kind: "numeric",
80012
+ field: "value",
80013
+ quantity: t("the total", "il totale"),
80014
+ label: t("Total", "Totale"),
80015
+ icon: "gauge"
80016
+ }]
80017
+ },
79571
80018
  "numeric-sensor": {
79572
80019
  scope: "device",
79573
80020
  noun: t("Sensor", "Sensore"),
@@ -80040,8 +80487,47 @@ function projectAction(cap, action) {
80040
80487
  };
80041
80488
  }
80042
80489
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
80043
- 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;
80490
+ 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;
80044
80491
  DeviceFeature.BatteryOperated;
80492
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
80493
+ 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.";
80494
+ 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.";
80495
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
80496
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
80497
+ 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.";
80498
+ function withheld(reason) {
80499
+ return {
80500
+ [DeviceType.Washer]: {
80501
+ exported: false,
80502
+ reason
80503
+ },
80504
+ [DeviceType.Dryer]: {
80505
+ exported: false,
80506
+ reason
80507
+ },
80508
+ [DeviceType.Dishwasher]: {
80509
+ exported: false,
80510
+ reason
80511
+ }
80512
+ };
80513
+ }
80514
+ function offered(received) {
80515
+ return {
80516
+ [DeviceType.Washer]: {
80517
+ exported: true,
80518
+ as: received
80519
+ },
80520
+ [DeviceType.Dryer]: {
80521
+ exported: true,
80522
+ as: received
80523
+ },
80524
+ [DeviceType.Dishwasher]: {
80525
+ exported: true,
80526
+ as: received
80527
+ }
80528
+ };
80529
+ }
80530
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
80045
80531
  /** Reject after `ms`; always clears its own timer. */
80046
80532
  async function withTimeout$1(promise, ms, label) {
80047
80533
  let timer;
@@ -81074,6 +81560,11 @@ Object.freeze({
81074
81560
  form: "single",
81075
81561
  optional: false
81076
81562
  }],
81563
+ "groupActive.getStatus": [{
81564
+ name: "deviceId",
81565
+ form: "single",
81566
+ optional: false
81567
+ }],
81077
81568
  "groupCommands.getStatus": [{
81078
81569
  name: "deviceId",
81079
81570
  form: "single",
@@ -81284,6 +81775,11 @@ Object.freeze({
81284
81775
  form: "single",
81285
81776
  optional: false
81286
81777
  }],
81778
+ "meter.getStatus": [{
81779
+ name: "deviceId",
81780
+ form: "single",
81781
+ optional: false
81782
+ }],
81287
81783
  "motion.getStatus": [{
81288
81784
  name: "deviceId",
81289
81785
  form: "single",
@@ -82891,7 +83387,6 @@ Object.freeze({
82891
83387
  "network-access": "ingress",
82892
83388
  "smtp-provider": "email"
82893
83389
  });
82894
- DeviceType.Camera;
82895
83390
  /**
82896
83391
  * A saved Customize assignment (D936). The schema is the type: callers use
82897
83392
  * `z.infer`, they do not restate the shape.