@camstack/addon-pipeline-orchestrator 1.2.631 → 1.2.632

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/index.js CHANGED
@@ -5606,7 +5606,7 @@ var ZodIssueCode = {
5606
5606
  var ZodFirstPartyTypeKind;
5607
5607
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5608
5608
  //#endregion
5609
- //#region ../types/dist/sleep-bykKrTgu.mjs
5609
+ //#region ../types/dist/sleep--jXS9hWo.mjs
5610
5610
  /**
5611
5611
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5612
5612
  * window to float samples (D455).
@@ -9513,6 +9513,7 @@ var CAP_NAMES = Object.freeze([
9513
9513
  "filesystem-browse",
9514
9514
  "flood",
9515
9515
  "gas",
9516
+ "group-active",
9516
9517
  "group-commands",
9517
9518
  "group-status",
9518
9519
  "heating-valve",
@@ -9535,6 +9536,7 @@ var CAP_NAMES = Object.freeze([
9535
9536
  "login-method",
9536
9537
  "media-player",
9537
9538
  "mesh-network",
9539
+ "meter",
9538
9540
  "metrics-provider",
9539
9541
  "model-convert",
9540
9542
  "model-distributor",
@@ -10333,6 +10335,10 @@ Object.freeze({
10333
10335
  "path": "detected",
10334
10336
  "valueType": "boolean"
10335
10337
  }],
10338
+ "group-active": [{
10339
+ "path": "active",
10340
+ "valueType": "boolean"
10341
+ }],
10336
10342
  "group-status": [
10337
10343
  {
10338
10344
  "path": "included",
@@ -10592,6 +10598,10 @@ Object.freeze({
10592
10598
  ]
10593
10599
  }
10594
10600
  ],
10601
+ "meter": [{
10602
+ "path": "value",
10603
+ "valueType": "number"
10604
+ }],
10595
10605
  "motion": [
10596
10606
  {
10597
10607
  "path": "detected",
@@ -11359,7 +11369,7 @@ function sleep(ms) {
11359
11369
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11360
11370
  }
11361
11371
  //#endregion
11362
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
11372
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
11363
11373
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
11364
11374
  DeviceType["Camera"] = "camera";
11365
11375
  DeviceType["Hub"] = "hub";
@@ -11453,8 +11463,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
11453
11463
  DeviceType["PetFeeder"] = "pet-feeder";
11454
11464
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
11455
11465
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
11456
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
11466
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
11467
+ * A plug-built dishwasher carries running-state and power instead. */
11457
11468
  DeviceType["Dishwasher"] = "dishwasher";
11469
+ /** Washing machine. A plug-built one is running while its power stays
11470
+ * above a threshold. No program cap — that surface is the dishwasher's. */
11471
+ DeviceType["Washer"] = "washer";
11472
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
11473
+ DeviceType["Dryer"] = "dryer";
11458
11474
  return DeviceType;
11459
11475
  }({});
11460
11476
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11712,7 +11728,7 @@ function errMsg$2(err) {
11712
11728
  return String(err);
11713
11729
  }
11714
11730
  //#endregion
11715
- //#region ../types/dist/composition-OhpuespA.mjs
11731
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
11716
11732
  /**
11717
11733
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11718
11734
  *
@@ -12156,6 +12172,13 @@ var table = {
12156
12172
  apply: () => {
12157
12173
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
12158
12174
  }
12175
+ },
12176
+ schmitt: {
12177
+ minArgs: 3,
12178
+ maxArgs: 5,
12179
+ apply: () => {
12180
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
12181
+ }
12159
12182
  }
12160
12183
  };
12161
12184
  Object.freeze(Object.assign(Object.create(null), table));
@@ -12597,6 +12620,99 @@ function validateExpressionSource(src) {
12597
12620
  return null;
12598
12621
  }
12599
12622
  /**
12623
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
12624
+ *
12625
+ * Standby of the control board is under 5 W; a motor or a heater is far
12626
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
12627
+ * a cycle, so running starts only after 30 s above the line.
12628
+ *
12629
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
12630
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
12631
+ * report "finished" in the middle of the program, so the stop dwell is
12632
+ * 10 minutes on every type. The number is stored per type so one machine
12633
+ * can move later without moving the others.
12634
+ */
12635
+ var APPLIANCE_TEMPLATE_ID = "appliance";
12636
+ var APPLIANCE_KINDS = [
12637
+ "washer",
12638
+ "dryer",
12639
+ "dishwasher"
12640
+ ];
12641
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
12642
+ /**
12643
+ * Statistics and integrals over a history read (D944).
12644
+ *
12645
+ * Pure: the same points always give the same answer, so a restart recomputes
12646
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
12647
+ * never the mean of the samples. A hole with no held value is not integrated
12648
+ * as zero. A truncated read is not published.
12649
+ */
12650
+ var STATISTIC_STATS = [
12651
+ "min",
12652
+ "max",
12653
+ "mean",
12654
+ "sum",
12655
+ "count",
12656
+ "change"
12657
+ ];
12658
+ /**
12659
+ * The range a statistics or integral helper asks history for (D944).
12660
+ *
12661
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
12662
+ * or 25 hours across a DST change. A tier is eligible only when its retention
12663
+ * is strictly longer than the longest that range can be. Forever qualifies.
12664
+ * Equal to a tier's retention is a refusal, not a short answer.
12665
+ */
12666
+ var MINUTE_MS = 6e4;
12667
+ var HOUR_MS = 60 * MINUTE_MS;
12668
+ var DAY_MS = 24 * HOUR_MS;
12669
+ var ROLLING_SPANS = [
12670
+ "1h",
12671
+ "24h",
12672
+ "2d",
12673
+ "7d",
12674
+ "30d",
12675
+ "custom"
12676
+ ];
12677
+ var CALENDAR_PERIODS = [
12678
+ "today",
12679
+ "yesterday",
12680
+ "this-week",
12681
+ "this-month"
12682
+ ];
12683
+ var RESET_PERIODS = [
12684
+ "never",
12685
+ "daily",
12686
+ "weekly",
12687
+ "monthly"
12688
+ ];
12689
+ 5 * MINUTE_MS;
12690
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
12691
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
12692
+ var rollingWindowObject = object({
12693
+ kind: literal("rolling"),
12694
+ span: _enum(ROLLING_SPANS),
12695
+ customMinutes: number().int().min(5).optional()
12696
+ }).check((payload) => {
12697
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
12698
+ code: "custom",
12699
+ message: "a custom range needs a duration in minutes",
12700
+ path: ["customMinutes"],
12701
+ input: payload.value
12702
+ });
12703
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
12704
+ code: "custom",
12705
+ message: "only a custom range carries a duration",
12706
+ path: ["customMinutes"],
12707
+ input: payload.value
12708
+ });
12709
+ });
12710
+ var RollingWindowSchema = rollingWindowObject;
12711
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
12712
+ kind: literal("calendar"),
12713
+ period: _enum(CALENDAR_PERIODS)
12714
+ })]);
12715
+ /**
12600
12716
  * A composition source identity: `(addonId, stableId)`, never a numeric device
12601
12717
  * id. Split out so helper schemas can name it without a cycle through
12602
12718
  * `composition.ts` (D659).
@@ -12622,7 +12738,13 @@ var GROUP_TEMPLATE_IDS = [
12622
12738
  "occupancy",
12623
12739
  "binary",
12624
12740
  "switch",
12625
- "light"
12741
+ "light",
12742
+ "fan",
12743
+ "cover",
12744
+ "valve",
12745
+ "lock",
12746
+ "media-player",
12747
+ "climate"
12626
12748
  ];
12627
12749
  var NUMERIC_AGGREGATES = [
12628
12750
  "mean",
@@ -12652,14 +12774,73 @@ var HelperMemberSchema = object({
12652
12774
  cap: string().min(1),
12653
12775
  fieldPath: string().min(1)
12654
12776
  });
12655
- var HelperSpecSchema = discriminatedUnion("kind", [object({
12656
- kind: literal("group"),
12657
- templateId: GroupTemplateIdSchema,
12658
- aggregate: AggregateReducerSchema,
12659
- members: array(HelperMemberSchema).min(1).max(64),
12660
- /** Actuator groups only. Readings leave this unset. */
12661
- active: ActiveCriterionSchema.optional()
12662
- })]);
12777
+ var HelperSpecSchema = discriminatedUnion("kind", [
12778
+ object({
12779
+ kind: literal("group"),
12780
+ templateId: GroupTemplateIdSchema,
12781
+ aggregate: AggregateReducerSchema,
12782
+ members: array(HelperMemberSchema).min(1).max(64),
12783
+ /** Actuator groups only. Readings leave this unset. */
12784
+ active: ActiveCriterionSchema.optional()
12785
+ }),
12786
+ object({
12787
+ kind: literal("appliance"),
12788
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
12789
+ appliance: _enum(APPLIANCE_KINDS),
12790
+ thresholdWatts: number().positive(),
12791
+ startDwellMs: number().int().nonnegative(),
12792
+ stopDwellMs: number().int().nonnegative(),
12793
+ member: HelperMemberSchema
12794
+ }),
12795
+ object({
12796
+ kind: literal("statistic"),
12797
+ stat: _enum(STATISTIC_STATS),
12798
+ window: StatisticWindowSchema,
12799
+ source: HelperMemberSchema
12800
+ }),
12801
+ object({
12802
+ kind: literal("integral"),
12803
+ reset: _enum(RESET_PERIODS),
12804
+ output: _enum(["energy", "meter"]),
12805
+ source: HelperMemberSchema
12806
+ }),
12807
+ object({
12808
+ kind: literal("threshold"),
12809
+ source: HelperMemberSchema,
12810
+ high: number(),
12811
+ low: number(),
12812
+ onForMs: number().int().nonnegative(),
12813
+ offForMs: number().int().nonnegative()
12814
+ }),
12815
+ object({
12816
+ kind: literal("arithmetic"),
12817
+ op: _enum([
12818
+ "sum",
12819
+ "difference",
12820
+ "ratio"
12821
+ ]),
12822
+ inputs: array(HelperMemberSchema).min(2).max(64)
12823
+ }),
12824
+ object({
12825
+ kind: literal("expression"),
12826
+ expr: string().min(1),
12827
+ inputs: array(object({
12828
+ name: string().min(1),
12829
+ member: HelperMemberSchema
12830
+ })).min(1).max(64),
12831
+ output: _enum(["number", "boolean"])
12832
+ }),
12833
+ object({
12834
+ kind: literal("derivative"),
12835
+ window: RollingWindowSchema,
12836
+ per: _enum([
12837
+ "second",
12838
+ "minute",
12839
+ "hour"
12840
+ ]),
12841
+ source: HelperMemberSchema
12842
+ })
12843
+ ]);
12663
12844
  /**
12664
12845
  * A COMPOSITION: a device assembled from other devices' state (D659).
12665
12846
  *
@@ -12741,6 +12922,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12741
12922
  "all"
12742
12923
  ]),
12743
12924
  reads: array(CompositionFieldReadSchema).min(1).max(64),
12925
+ /**
12926
+ * Set when the reads are an enum state, not a yes/no: the listed states
12927
+ * count as active (a cover is `open`). A state outside the list is inactive;
12928
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
12929
+ */
12930
+ activeWhen: array(string().min(1)).min(1).optional(),
12744
12931
  /** `value` is the reading; the others are the member counts. */
12745
12932
  publish: _enum([
12746
12933
  "value",
@@ -12754,6 +12941,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12754
12941
  "omit",
12755
12942
  "offline"
12756
12943
  ])
12944
+ }),
12945
+ object({
12946
+ kind: literal("series"),
12947
+ read: CompositionFieldReadSchema,
12948
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12949
+ publish: _enum([
12950
+ "value",
12951
+ "included",
12952
+ "excluded",
12953
+ "total"
12954
+ ]),
12955
+ /**
12956
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12957
+ * device unseeded until history answers, so a restart cannot publish 0.
12958
+ */
12959
+ empty: _enum([
12960
+ "null",
12961
+ "omit",
12962
+ "offline"
12963
+ ])
12757
12964
  })
12758
12965
  ]);
12759
12966
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -18262,7 +18469,22 @@ var BrokerProviderInfoSchema = object({
18262
18469
  */
18263
18470
  interactiveConnectPath: string().min(1).optional(),
18264
18471
  /** Relative hub path to disconnect the linked account (same popup pattern). */
18265
- interactiveDisconnectPath: string().min(1).optional()
18472
+ interactiveDisconnectPath: string().min(1).optional(),
18473
+ /**
18474
+ * The kind signs in through a server-held interactive session instead of
18475
+ * (or before) typed credentials. `basePath` is a hub-relative route set
18476
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
18477
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
18478
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
18479
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
18480
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
18481
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
18482
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
18483
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
18484
+ * offers it as the default path; the fields the session fills are
18485
+ * marked `filledBySignIn` in the kind's settings schema.
18486
+ */
18487
+ signIn: object({ basePath: string().min(1) }).optional()
18266
18488
  }))
18267
18489
  });
18268
18490
  var ListInputSchema = object({
@@ -19299,7 +19521,8 @@ var CompositionProblemSchema = object({
19299
19521
  "native-capability-replace",
19300
19522
  "mixed-quantity",
19301
19523
  "helper-mismatch",
19302
- "member-constraint"
19524
+ "member-constraint",
19525
+ "retention"
19303
19526
  ]),
19304
19527
  path: string(),
19305
19528
  message: string()
@@ -19533,13 +19756,17 @@ Object.freeze({
19533
19756
  latchedAt: {
19534
19757
  kind: "fixed",
19535
19758
  result: ["number", "null"]
19759
+ },
19760
+ schmitt: {
19761
+ kind: "fixed",
19762
+ result: ["boolean", "null"]
19536
19763
  }
19537
19764
  });
19538
19765
  /**
19539
19766
  * One catalog row per base reading a group helper can build (D944 §3).
19540
19767
  * Data, not a form default written into the block.
19541
19768
  */
19542
- var NOW = {
19769
+ var NOW$2 = {
19543
19770
  fieldPath: "lastFetchedAt",
19544
19771
  expr: "now"
19545
19772
  };
@@ -19553,6 +19780,8 @@ function numeric(input) {
19553
19780
  family: "numeric",
19554
19781
  deviceType: DeviceType.Sensor,
19555
19782
  memberDeviceType: DeviceType.Sensor,
19783
+ memberDeviceTypes: [DeviceType.Sensor],
19784
+ activeWhen: void 0,
19556
19785
  commandCaps: [],
19557
19786
  baseUnit: baseUnitOf(input.quantity),
19558
19787
  aggregates: NUMERIC_AGGREGATES,
@@ -19568,6 +19797,8 @@ function binary(input) {
19568
19797
  family: "binary",
19569
19798
  deviceType: DeviceType.Sensor,
19570
19799
  memberDeviceType: DeviceType.Sensor,
19800
+ memberDeviceTypes: [DeviceType.Sensor],
19801
+ activeWhen: void 0,
19571
19802
  commandCaps: [],
19572
19803
  quantity: "none",
19573
19804
  baseUnit: void 0,
@@ -19585,7 +19816,7 @@ var GROUP_TEMPLATES = [
19585
19816
  quantity: "temperature",
19586
19817
  defaultAggregate: "mean",
19587
19818
  empty: "null",
19588
- companions: [NOW]
19819
+ companions: [NOW$2]
19589
19820
  }),
19590
19821
  numeric({
19591
19822
  id: "humidity",
@@ -19596,7 +19827,7 @@ var GROUP_TEMPLATES = [
19596
19827
  quantity: "humidity",
19597
19828
  defaultAggregate: "mean",
19598
19829
  empty: "null",
19599
- companions: [NOW]
19830
+ companions: [NOW$2]
19600
19831
  }),
19601
19832
  numeric({
19602
19833
  id: "pressure",
@@ -19607,7 +19838,7 @@ var GROUP_TEMPLATES = [
19607
19838
  quantity: "pressure",
19608
19839
  defaultAggregate: "mean",
19609
19840
  empty: "null",
19610
- companions: [NOW]
19841
+ companions: [NOW$2]
19611
19842
  }),
19612
19843
  numeric({
19613
19844
  id: "illuminance",
@@ -19618,7 +19849,7 @@ var GROUP_TEMPLATES = [
19618
19849
  quantity: "illuminance",
19619
19850
  defaultAggregate: "mean",
19620
19851
  empty: "null",
19621
- companions: [NOW]
19852
+ companions: [NOW$2]
19622
19853
  }),
19623
19854
  numeric({
19624
19855
  id: "power",
@@ -19629,7 +19860,7 @@ var GROUP_TEMPLATES = [
19629
19860
  quantity: "power",
19630
19861
  defaultAggregate: "sum",
19631
19862
  empty: "omit",
19632
- companions: [NOW]
19863
+ companions: [NOW$2]
19633
19864
  }),
19634
19865
  numeric({
19635
19866
  id: "energy",
@@ -19640,7 +19871,7 @@ var GROUP_TEMPLATES = [
19640
19871
  quantity: "energy",
19641
19872
  defaultAggregate: "sum",
19642
19873
  empty: "omit",
19643
- companions: [NOW]
19874
+ companions: [NOW$2]
19644
19875
  }),
19645
19876
  numeric({
19646
19877
  id: "battery",
@@ -19675,7 +19906,7 @@ var GROUP_TEMPLATES = [
19675
19906
  quantity: "device",
19676
19907
  defaultAggregate: "mean",
19677
19908
  empty: "null",
19678
- companions: [NOW]
19909
+ companions: [NOW$2]
19679
19910
  }),
19680
19911
  binary({
19681
19912
  id: "motion",
@@ -19769,6 +20000,11 @@ var GROUP_TEMPLATES = [
19769
20000
  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.",
19770
20001
  deviceType: DeviceType.Switch,
19771
20002
  outputCap: "switch",
20003
+ outputField: "on",
20004
+ member: {
20005
+ cap: "switch",
20006
+ fieldPath: "on"
20007
+ },
19772
20008
  commandCaps: ["switch"]
19773
20009
  }),
19774
20010
  actuator({
@@ -19776,34 +20012,136 @@ var GROUP_TEMPLATES = [
19776
20012
  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.",
19777
20013
  deviceType: DeviceType.Light,
19778
20014
  outputCap: "switch",
20015
+ outputField: "on",
20016
+ member: {
20017
+ cap: "switch",
20018
+ fieldPath: "on"
20019
+ },
19779
20020
  commandCaps: [
19780
20021
  "switch",
19781
20022
  "brightness",
19782
20023
  "color"
19783
20024
  ]
20025
+ }),
20026
+ actuator({
20027
+ id: "fan",
20028
+ 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.",
20029
+ deviceType: DeviceType.Fan,
20030
+ outputCap: "switch",
20031
+ outputField: "on",
20032
+ member: {
20033
+ cap: "switch",
20034
+ fieldPath: "on"
20035
+ },
20036
+ commandCaps: ["switch", "fan-control"]
20037
+ }),
20038
+ actuator({
20039
+ id: "cover",
20040
+ 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.",
20041
+ deviceType: DeviceType.Cover,
20042
+ outputCap: "group-active",
20043
+ outputField: "active",
20044
+ member: {
20045
+ cap: "cover",
20046
+ fieldPath: "state"
20047
+ },
20048
+ activeWhen: [
20049
+ "open",
20050
+ "opening",
20051
+ "stopped"
20052
+ ],
20053
+ commandCaps: ["cover"]
20054
+ }),
20055
+ actuator({
20056
+ id: "valve",
20057
+ 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.",
20058
+ deviceType: DeviceType.Valve,
20059
+ outputCap: "group-active",
20060
+ outputField: "active",
20061
+ member: {
20062
+ cap: "valve",
20063
+ fieldPath: "state"
20064
+ },
20065
+ activeWhen: [
20066
+ "open",
20067
+ "opening",
20068
+ "stopped"
20069
+ ],
20070
+ commandCaps: ["valve"]
20071
+ }),
20072
+ actuator({
20073
+ id: "lock",
20074
+ 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.",
20075
+ deviceType: DeviceType.Lock,
20076
+ outputCap: "group-active",
20077
+ outputField: "active",
20078
+ member: {
20079
+ cap: "lock-control",
20080
+ fieldPath: "state"
20081
+ },
20082
+ activeWhen: ["locked"],
20083
+ commandCaps: ["lock-control"]
20084
+ }),
20085
+ actuator({
20086
+ id: "media-player",
20087
+ 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.",
20088
+ deviceType: DeviceType.MediaPlayer,
20089
+ outputCap: "group-active",
20090
+ outputField: "active",
20091
+ member: {
20092
+ cap: "media-player",
20093
+ fieldPath: "state"
20094
+ },
20095
+ activeWhen: ["playing", "buffering"],
20096
+ commandCaps: ["media-player"]
20097
+ }),
20098
+ actuator({
20099
+ id: "climate",
20100
+ 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.",
20101
+ deviceType: DeviceType.Thermostat,
20102
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
20103
+ outputCap: "group-active",
20104
+ outputField: "active",
20105
+ member: {
20106
+ cap: "climate-control",
20107
+ fieldPath: "mode"
20108
+ },
20109
+ activeWhen: [
20110
+ "heat",
20111
+ "cool",
20112
+ "auto",
20113
+ "heat_cool",
20114
+ "fan_only",
20115
+ "dry"
20116
+ ],
20117
+ commandCaps: ["climate-control"]
19784
20118
  })
19785
20119
  ];
19786
20120
  function actuator(input) {
19787
20121
  return {
19788
- ...input,
20122
+ id: input.id,
20123
+ summary: input.summary,
20124
+ deviceType: input.deviceType,
20125
+ outputCap: input.outputCap,
20126
+ outputField: input.outputField,
20127
+ commandCaps: input.commandCaps,
19789
20128
  family: "actuator",
19790
20129
  role: void 0,
19791
20130
  memberDeviceType: input.deviceType,
19792
- outputField: "on",
20131
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
20132
+ activeWhen: input.activeWhen,
19793
20133
  quantity: "none",
19794
20134
  baseUnit: void 0,
19795
20135
  aggregates: BINARY_AGGREGATES,
19796
20136
  defaultAggregate: "any",
19797
20137
  empty: "offline",
19798
- accept: [{
19799
- cap: "switch",
19800
- fieldPath: "on"
19801
- }],
20138
+ accept: [input.member],
19802
20139
  companions: [CHANGED]
19803
20140
  };
19804
20141
  }
19805
20142
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
19806
20143
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
20144
+ DeviceType.Camera;
19807
20145
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19808
20146
  /**
19809
20147
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -44818,7 +45156,7 @@ var NetworkAccessStatusSchema = object({
44818
45156
  });
44819
45157
  /**
44820
45158
  * Optional, richer endpoint shape returned by providers that expose
44821
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
45159
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44822
45160
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44823
45161
  * the originating provider config (mode + sourcePort) so the
44824
45162
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -47729,7 +48067,20 @@ var NcRuleInputSchema = object({
47729
48067
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47730
48068
  * path does not run Zod defaults.
47731
48069
  */
47732
- groupingSec: number().int().min(5).max(60).optional()
48070
+ groupingSec: number().int().min(5).max(60).optional(),
48071
+ /**
48072
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
48073
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
48074
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
48075
+ * same mark the Library writes, and records whose notification it was and
48076
+ * when. A channel with no buttons gets the same two choices as links.
48077
+ *
48078
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
48079
+ * cap path): absent and `false` both mean off, which is what every rule
48080
+ * authored before this field meant. An editor turns it off by writing
48081
+ * `false`, so it needs no `clear` key.
48082
+ */
48083
+ askVerdict: boolean().optional()
47733
48084
  });
47734
48085
  /**
47735
48086
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -59109,7 +59460,7 @@ var BatteryStatusSchema = object({
59109
59460
  */
59110
59461
  binary: boolean().optional().meta({ label: "Binary indicator" })
59111
59462
  });
59112
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat;
59463
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter;
59113
59464
  method(object({
59114
59465
  deviceId: number(),
59115
59466
  /** Bound on the wait. Sensible range 3000–10000ms. */
@@ -60862,7 +61213,13 @@ results: array(object({
60862
61213
  /** Ms epoch of the attempt. */
60863
61214
  at: number()
60864
61215
  })) });
60865
- DeviceType.Switch, DeviceType.Light;
61216
+ DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
61217
+ object({
61218
+ active: boolean().meta({ label: "Active" }),
61219
+ /** Ms epoch when the slice was last updated. */
61220
+ lastChangedAt: number()
61221
+ });
61222
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
60866
61223
  object({
60867
61224
  /** Members whose current value entered the aggregate. */
60868
61225
  included: number().int().nonnegative().meta(readingMeta("count", {
@@ -60880,7 +61237,7 @@ object({
60880
61237
  label: "Members"
60881
61238
  }))
60882
61239
  });
60883
- DeviceType.Sensor, DeviceType.Switch, DeviceType.Light;
61240
+ DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
60884
61241
  object({
60885
61242
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
60886
61243
  position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -61787,7 +62144,14 @@ DeviceType.Lock, method(object({
61787
62144
  auth: "protected"
61788
62145
  }), method(object({
61789
62146
  deviceId: number().int().nonnegative(),
61790
- code: string().min(1).optional()
62147
+ code: string().min(1).optional(),
62148
+ /**
62149
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62150
+ * with one call is refused unless the caller says it was confirmed. A
62151
+ * single lock ignores it. The refusal lives in the callee, so no
62152
+ * automation or API caller reaches the doors by skipping a dialog.
62153
+ */
62154
+ confirm: boolean().optional()
61791
62155
  }), _void(), {
61792
62156
  kind: "mutation",
61793
62157
  auth: "protected"
@@ -61795,6 +62159,11 @@ DeviceType.Lock, method(object({
61795
62159
  kind: "mutation",
61796
62160
  auth: "protected"
61797
62161
  });
62162
+ object({
62163
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
62164
+ lastFetchedAt: number()
62165
+ });
62166
+ DeviceType.Sensor;
61798
62167
  /**
61799
62168
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
61800
62169
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
@@ -64443,9 +64812,9 @@ object({
64443
64812
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64444
64813
  kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64445
64814
  /** Voltage in volts. */
64446
- volts: number().optional().meta(readingMeta("voltage")),
64815
+ volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64447
64816
  /** Current in amperes. */
64448
- amps: number().optional().meta(readingMeta("current")),
64817
+ amps: number().optional().meta(readingMeta("current", { label: "Current" })),
64449
64818
  /** Apparent power in volt-amperes. */
64450
64819
  va: number().optional().meta(readingMeta("apparent-power")),
64451
64820
  /** Power factor (0..1) if reported. */
@@ -64453,7 +64822,23 @@ object({
64453
64822
  /** Ms epoch when the slice was last updated. */
64454
64823
  lastFetchedAt: number()
64455
64824
  });
64456
- DeviceType.Sensor;
64825
+ [
64826
+ DeviceType.Sensor,
64827
+ DeviceType.Switch,
64828
+ DeviceType.Light,
64829
+ DeviceType.Fan,
64830
+ DeviceType.Climate,
64831
+ DeviceType.Thermostat,
64832
+ DeviceType.Humidifier,
64833
+ DeviceType.WaterHeater,
64834
+ DeviceType.MediaPlayer,
64835
+ DeviceType.Washer,
64836
+ DeviceType.Dryer,
64837
+ DeviceType.Dishwasher,
64838
+ DeviceType.Vacuum,
64839
+ DeviceType.LawnMower,
64840
+ DeviceType.PetFeeder
64841
+ ].filter((type) => type !== DeviceType.Sensor);
64457
64842
  /**
64458
64843
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
64459
64844
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -64900,7 +65285,7 @@ object({
64900
65285
  phase: string().nullable()
64901
65286
  });
64902
65287
  Object.values(DeviceType);
64903
- DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
65288
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
64904
65289
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
64905
65290
  kind: "mutation",
64906
65291
  auth: "admin"
@@ -66973,7 +67358,7 @@ object({
66973
67358
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
66974
67359
  lastChangedAt: number()
66975
67360
  });
66976
- DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
67361
+ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
66977
67362
  deviceId: number(),
66978
67363
  on: boolean()
66979
67364
  }), _void(), {
@@ -68725,8 +69110,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
68725
69110
  kind: "mutation",
68726
69111
  auth: "admin"
68727
69112
  });
69113
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
68728
69114
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
68729
- 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;
69115
+ 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;
68730
69116
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
68731
69117
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
68732
69118
  new Set(CAP_NAMES);
@@ -68779,7 +69165,7 @@ var AccessoryKind = {
68779
69165
  PrivacyMask: DeviceRole.PrivacyMask
68780
69166
  };
68781
69167
  AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
68782
- 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;
69168
+ 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;
68783
69169
  new Set(Object.values(DeviceType));
68784
69170
  Object.freeze({
68785
69171
  "accessories.getStatus": {
@@ -72002,6 +72388,13 @@ Object.freeze({
72002
72388
  access: "view",
72003
72389
  role: "viewer"
72004
72390
  },
72391
+ "groupActive.getStatus": {
72392
+ capName: "group-active",
72393
+ capScope: "device",
72394
+ addonId: null,
72395
+ access: "view",
72396
+ role: "viewer"
72397
+ },
72005
72398
  "groupCommands.getStatus": {
72006
72399
  capName: "group-commands",
72007
72400
  capScope: "device",
@@ -72968,6 +73361,13 @@ Object.freeze({
72968
73361
  access: "create",
72969
73362
  role: "admin"
72970
73363
  },
73364
+ "meter.getStatus": {
73365
+ capName: "meter",
73366
+ capScope: "device",
73367
+ addonId: null,
73368
+ access: "view",
73369
+ role: "viewer"
73370
+ },
72971
73371
  "metricsProvider.collectSnapshot": {
72972
73372
  capName: "metrics-provider",
72973
73373
  capScope: "system",
@@ -81066,7 +81466,39 @@ var APPLIANCE_CAP_VOCABULARY = {
81066
81466
  phrase: t("When {device} finishes", "Quando {device} finisce"),
81067
81467
  icon: "square"
81068
81468
  }
81069
- }
81469
+ },
81470
+ overrides: { byType: {
81471
+ [DeviceType.Washer]: {
81472
+ state: { running: {
81473
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
81474
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
81475
+ } },
81476
+ events: {
81477
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
81478
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
81479
+ }
81480
+ },
81481
+ [DeviceType.Dryer]: {
81482
+ state: { running: {
81483
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
81484
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
81485
+ } },
81486
+ events: {
81487
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
81488
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
81489
+ }
81490
+ },
81491
+ [DeviceType.Dishwasher]: {
81492
+ state: { running: {
81493
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
81494
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
81495
+ } },
81496
+ events: {
81497
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
81498
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
81499
+ }
81500
+ }
81501
+ } }
81070
81502
  },
81071
81503
  update: {
81072
81504
  scope: "device",
@@ -81838,6 +82270,10 @@ var SENSOR_CAP_VOCABULARY = {
81838
82270
  icon: "flame"
81839
82271
  }]
81840
82272
  },
82273
+ "group-active": {
82274
+ scope: "device",
82275
+ noun: t("Group state", "Stato del gruppo")
82276
+ },
81841
82277
  "group-commands": {
81842
82278
  scope: "device",
81843
82279
  noun: t("Group command", "Comando di gruppo")
@@ -81894,6 +82330,17 @@ var SENSOR_CAP_VOCABULARY = {
81894
82330
  }],
81895
82331
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
81896
82332
  },
82333
+ meter: {
82334
+ scope: "device",
82335
+ noun: t("Meter", "Contatore"),
82336
+ state: [{
82337
+ kind: "numeric",
82338
+ field: "value",
82339
+ quantity: t("the total", "il totale"),
82340
+ label: t("Total", "Totale"),
82341
+ icon: "gauge"
82342
+ }]
82343
+ },
81897
82344
  "numeric-sensor": {
81898
82345
  scope: "device",
81899
82346
  noun: t("Sensor", "Sensore"),
@@ -82366,7 +82813,7 @@ function projectAction(cap, action) {
82366
82813
  };
82367
82814
  }
82368
82815
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
82369
- 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;
82816
+ 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;
82370
82817
  /**
82371
82818
  * Heuristic check — `device.features?.includes(profile.when.hasFeature)`.
82372
82819
  */
@@ -82396,6 +82843,45 @@ function resolveDeviceProfile(features) {
82396
82843
  for (const profile of DEVICE_PROFILES) if (deviceMatchesProfile(features, profile)) return profile;
82397
82844
  return null;
82398
82845
  }
82846
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
82847
+ 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.";
82848
+ 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.";
82849
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
82850
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
82851
+ 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.";
82852
+ function withheld(reason) {
82853
+ return {
82854
+ [DeviceType.Washer]: {
82855
+ exported: false,
82856
+ reason
82857
+ },
82858
+ [DeviceType.Dryer]: {
82859
+ exported: false,
82860
+ reason
82861
+ },
82862
+ [DeviceType.Dishwasher]: {
82863
+ exported: false,
82864
+ reason
82865
+ }
82866
+ };
82867
+ }
82868
+ function offered(received) {
82869
+ return {
82870
+ [DeviceType.Washer]: {
82871
+ exported: true,
82872
+ as: received
82873
+ },
82874
+ [DeviceType.Dryer]: {
82875
+ exported: true,
82876
+ as: received
82877
+ },
82878
+ [DeviceType.Dishwasher]: {
82879
+ exported: true,
82880
+ as: received
82881
+ }
82882
+ };
82883
+ }
82884
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
82399
82885
  Object.freeze({
82400
82886
  "accessories.getStatus": [{
82401
82887
  name: "deviceId",
@@ -83362,6 +83848,11 @@ Object.freeze({
83362
83848
  form: "single",
83363
83849
  optional: false
83364
83850
  }],
83851
+ "groupActive.getStatus": [{
83852
+ name: "deviceId",
83853
+ form: "single",
83854
+ optional: false
83855
+ }],
83365
83856
  "groupCommands.getStatus": [{
83366
83857
  name: "deviceId",
83367
83858
  form: "single",
@@ -83572,6 +84063,11 @@ Object.freeze({
83572
84063
  form: "single",
83573
84064
  optional: false
83574
84065
  }],
84066
+ "meter.getStatus": [{
84067
+ name: "deviceId",
84068
+ form: "single",
84069
+ optional: false
84070
+ }],
83575
84071
  "motion.getStatus": [{
83576
84072
  name: "deviceId",
83577
84073
  form: "single",
@@ -85276,7 +85772,6 @@ var BucketAccumulator = class {
85276
85772
  return n;
85277
85773
  }
85278
85774
  };
85279
- DeviceType.Camera;
85280
85775
  /**
85281
85776
  * A saved Customize assignment (D936). The schema is the type: callers use
85282
85777
  * `z.infer`, they do not restate the shape.