@camstack/addon-provider-rademacher 0.2.147 → 0.2.148

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
@@ -6427,7 +6427,7 @@ var ZodIssueCode = {
6427
6427
  var ZodFirstPartyTypeKind;
6428
6428
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6429
6429
  //#endregion
6430
- //#region ../types/dist/sleep-bykKrTgu.mjs
6430
+ //#region ../types/dist/sleep--jXS9hWo.mjs
6431
6431
  /**
6432
6432
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6433
6433
  * window to float samples (D455).
@@ -9620,6 +9620,7 @@ var CAP_NAMES = Object.freeze([
9620
9620
  "filesystem-browse",
9621
9621
  "flood",
9622
9622
  "gas",
9623
+ "group-active",
9623
9624
  "group-commands",
9624
9625
  "group-status",
9625
9626
  "heating-valve",
@@ -9642,6 +9643,7 @@ var CAP_NAMES = Object.freeze([
9642
9643
  "login-method",
9643
9644
  "media-player",
9644
9645
  "mesh-network",
9646
+ "meter",
9645
9647
  "metrics-provider",
9646
9648
  "model-convert",
9647
9649
  "model-distributor",
@@ -10440,6 +10442,10 @@ Object.freeze({
10440
10442
  "path": "detected",
10441
10443
  "valueType": "boolean"
10442
10444
  }],
10445
+ "group-active": [{
10446
+ "path": "active",
10447
+ "valueType": "boolean"
10448
+ }],
10443
10449
  "group-status": [
10444
10450
  {
10445
10451
  "path": "included",
@@ -10699,6 +10705,10 @@ Object.freeze({
10699
10705
  ]
10700
10706
  }
10701
10707
  ],
10708
+ "meter": [{
10709
+ "path": "value",
10710
+ "valueType": "number"
10711
+ }],
10702
10712
  "motion": [
10703
10713
  {
10704
10714
  "path": "detected",
@@ -11442,7 +11452,7 @@ object({
11442
11452
  params: record(string(), union([string(), number()])).optional()
11443
11453
  });
11444
11454
  //#endregion
11445
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
11455
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
11446
11456
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
11447
11457
  DeviceType["Camera"] = "camera";
11448
11458
  DeviceType["Hub"] = "hub";
@@ -11536,8 +11546,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
11536
11546
  DeviceType["PetFeeder"] = "pet-feeder";
11537
11547
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
11538
11548
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
11539
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
11549
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
11550
+ * A plug-built dishwasher carries running-state and power instead. */
11540
11551
  DeviceType["Dishwasher"] = "dishwasher";
11552
+ /** Washing machine. A plug-built one is running while its power stays
11553
+ * above a threshold. No program cap — that surface is the dishwasher's. */
11554
+ DeviceType["Washer"] = "washer";
11555
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
11556
+ DeviceType["Dryer"] = "dryer";
11541
11557
  return DeviceType;
11542
11558
  }({});
11543
11559
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11795,7 +11811,7 @@ function errMsg(err) {
11795
11811
  return String(err);
11796
11812
  }
11797
11813
  //#endregion
11798
- //#region ../types/dist/composition-OhpuespA.mjs
11814
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
11799
11815
  /**
11800
11816
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11801
11817
  *
@@ -12110,6 +12126,13 @@ var table = {
12110
12126
  apply: () => {
12111
12127
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
12112
12128
  }
12129
+ },
12130
+ schmitt: {
12131
+ minArgs: 3,
12132
+ maxArgs: 5,
12133
+ apply: () => {
12134
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
12135
+ }
12113
12136
  }
12114
12137
  };
12115
12138
  Object.freeze(Object.assign(Object.create(null), table));
@@ -12551,6 +12574,99 @@ function validateExpressionSource(src) {
12551
12574
  return null;
12552
12575
  }
12553
12576
  /**
12577
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
12578
+ *
12579
+ * Standby of the control board is under 5 W; a motor or a heater is far
12580
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
12581
+ * a cycle, so running starts only after 30 s above the line.
12582
+ *
12583
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
12584
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
12585
+ * report "finished" in the middle of the program, so the stop dwell is
12586
+ * 10 minutes on every type. The number is stored per type so one machine
12587
+ * can move later without moving the others.
12588
+ */
12589
+ var APPLIANCE_TEMPLATE_ID = "appliance";
12590
+ var APPLIANCE_KINDS = [
12591
+ "washer",
12592
+ "dryer",
12593
+ "dishwasher"
12594
+ ];
12595
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
12596
+ /**
12597
+ * Statistics and integrals over a history read (D944).
12598
+ *
12599
+ * Pure: the same points always give the same answer, so a restart recomputes
12600
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
12601
+ * never the mean of the samples. A hole with no held value is not integrated
12602
+ * as zero. A truncated read is not published.
12603
+ */
12604
+ var STATISTIC_STATS = [
12605
+ "min",
12606
+ "max",
12607
+ "mean",
12608
+ "sum",
12609
+ "count",
12610
+ "change"
12611
+ ];
12612
+ /**
12613
+ * The range a statistics or integral helper asks history for (D944).
12614
+ *
12615
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
12616
+ * or 25 hours across a DST change. A tier is eligible only when its retention
12617
+ * is strictly longer than the longest that range can be. Forever qualifies.
12618
+ * Equal to a tier's retention is a refusal, not a short answer.
12619
+ */
12620
+ var MINUTE_MS = 6e4;
12621
+ var HOUR_MS = 60 * MINUTE_MS;
12622
+ var DAY_MS = 24 * HOUR_MS;
12623
+ var ROLLING_SPANS = [
12624
+ "1h",
12625
+ "24h",
12626
+ "2d",
12627
+ "7d",
12628
+ "30d",
12629
+ "custom"
12630
+ ];
12631
+ var CALENDAR_PERIODS = [
12632
+ "today",
12633
+ "yesterday",
12634
+ "this-week",
12635
+ "this-month"
12636
+ ];
12637
+ var RESET_PERIODS = [
12638
+ "never",
12639
+ "daily",
12640
+ "weekly",
12641
+ "monthly"
12642
+ ];
12643
+ 5 * MINUTE_MS;
12644
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
12645
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
12646
+ var rollingWindowObject = object({
12647
+ kind: literal("rolling"),
12648
+ span: _enum(ROLLING_SPANS),
12649
+ customMinutes: number().int().min(5).optional()
12650
+ }).check((payload) => {
12651
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
12652
+ code: "custom",
12653
+ message: "a custom range needs a duration in minutes",
12654
+ path: ["customMinutes"],
12655
+ input: payload.value
12656
+ });
12657
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
12658
+ code: "custom",
12659
+ message: "only a custom range carries a duration",
12660
+ path: ["customMinutes"],
12661
+ input: payload.value
12662
+ });
12663
+ });
12664
+ var RollingWindowSchema = rollingWindowObject;
12665
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
12666
+ kind: literal("calendar"),
12667
+ period: _enum(CALENDAR_PERIODS)
12668
+ })]);
12669
+ /**
12554
12670
  * A composition source identity: `(addonId, stableId)`, never a numeric device
12555
12671
  * id. Split out so helper schemas can name it without a cycle through
12556
12672
  * `composition.ts` (D659).
@@ -12576,7 +12692,13 @@ var GROUP_TEMPLATE_IDS = [
12576
12692
  "occupancy",
12577
12693
  "binary",
12578
12694
  "switch",
12579
- "light"
12695
+ "light",
12696
+ "fan",
12697
+ "cover",
12698
+ "valve",
12699
+ "lock",
12700
+ "media-player",
12701
+ "climate"
12580
12702
  ];
12581
12703
  var NUMERIC_AGGREGATES = [
12582
12704
  "mean",
@@ -12606,14 +12728,73 @@ var HelperMemberSchema = object({
12606
12728
  cap: string().min(1),
12607
12729
  fieldPath: string().min(1)
12608
12730
  });
12609
- var HelperSpecSchema = discriminatedUnion("kind", [object({
12610
- kind: literal("group"),
12611
- templateId: GroupTemplateIdSchema,
12612
- aggregate: AggregateReducerSchema,
12613
- members: array(HelperMemberSchema).min(1).max(64),
12614
- /** Actuator groups only. Readings leave this unset. */
12615
- active: ActiveCriterionSchema.optional()
12616
- })]);
12731
+ var HelperSpecSchema = discriminatedUnion("kind", [
12732
+ object({
12733
+ kind: literal("group"),
12734
+ templateId: GroupTemplateIdSchema,
12735
+ aggregate: AggregateReducerSchema,
12736
+ members: array(HelperMemberSchema).min(1).max(64),
12737
+ /** Actuator groups only. Readings leave this unset. */
12738
+ active: ActiveCriterionSchema.optional()
12739
+ }),
12740
+ object({
12741
+ kind: literal("appliance"),
12742
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
12743
+ appliance: _enum(APPLIANCE_KINDS),
12744
+ thresholdWatts: number().positive(),
12745
+ startDwellMs: number().int().nonnegative(),
12746
+ stopDwellMs: number().int().nonnegative(),
12747
+ member: HelperMemberSchema
12748
+ }),
12749
+ object({
12750
+ kind: literal("statistic"),
12751
+ stat: _enum(STATISTIC_STATS),
12752
+ window: StatisticWindowSchema,
12753
+ source: HelperMemberSchema
12754
+ }),
12755
+ object({
12756
+ kind: literal("integral"),
12757
+ reset: _enum(RESET_PERIODS),
12758
+ output: _enum(["energy", "meter"]),
12759
+ source: HelperMemberSchema
12760
+ }),
12761
+ object({
12762
+ kind: literal("threshold"),
12763
+ source: HelperMemberSchema,
12764
+ high: number(),
12765
+ low: number(),
12766
+ onForMs: number().int().nonnegative(),
12767
+ offForMs: number().int().nonnegative()
12768
+ }),
12769
+ object({
12770
+ kind: literal("arithmetic"),
12771
+ op: _enum([
12772
+ "sum",
12773
+ "difference",
12774
+ "ratio"
12775
+ ]),
12776
+ inputs: array(HelperMemberSchema).min(2).max(64)
12777
+ }),
12778
+ object({
12779
+ kind: literal("expression"),
12780
+ expr: string().min(1),
12781
+ inputs: array(object({
12782
+ name: string().min(1),
12783
+ member: HelperMemberSchema
12784
+ })).min(1).max(64),
12785
+ output: _enum(["number", "boolean"])
12786
+ }),
12787
+ object({
12788
+ kind: literal("derivative"),
12789
+ window: RollingWindowSchema,
12790
+ per: _enum([
12791
+ "second",
12792
+ "minute",
12793
+ "hour"
12794
+ ]),
12795
+ source: HelperMemberSchema
12796
+ })
12797
+ ]);
12617
12798
  /** A composed device's stableId is this prefix plus the block id. */
12618
12799
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
12619
12800
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -12674,6 +12855,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12674
12855
  "all"
12675
12856
  ]),
12676
12857
  reads: array(CompositionFieldReadSchema).min(1).max(64),
12858
+ /**
12859
+ * Set when the reads are an enum state, not a yes/no: the listed states
12860
+ * count as active (a cover is `open`). A state outside the list is inactive;
12861
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
12862
+ */
12863
+ activeWhen: array(string().min(1)).min(1).optional(),
12677
12864
  /** `value` is the reading; the others are the member counts. */
12678
12865
  publish: _enum([
12679
12866
  "value",
@@ -12687,6 +12874,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12687
12874
  "omit",
12688
12875
  "offline"
12689
12876
  ])
12877
+ }),
12878
+ object({
12879
+ kind: literal("series"),
12880
+ read: CompositionFieldReadSchema,
12881
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12882
+ publish: _enum([
12883
+ "value",
12884
+ "included",
12885
+ "excluded",
12886
+ "total"
12887
+ ]),
12888
+ /**
12889
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12890
+ * device unseeded until history answers, so a restart cannot publish 0.
12891
+ */
12892
+ empty: _enum([
12893
+ "null",
12894
+ "omit",
12895
+ "offline"
12896
+ ])
12690
12897
  })
12691
12898
  ]);
12692
12899
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -17846,7 +18053,22 @@ var BrokerProviderInfoSchema = object({
17846
18053
  */
17847
18054
  interactiveConnectPath: string().min(1).optional(),
17848
18055
  /** Relative hub path to disconnect the linked account (same popup pattern). */
17849
- interactiveDisconnectPath: string().min(1).optional()
18056
+ interactiveDisconnectPath: string().min(1).optional(),
18057
+ /**
18058
+ * The kind signs in through a server-held interactive session instead of
18059
+ * (or before) typed credentials. `basePath` is a hub-relative route set
18060
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
18061
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
18062
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
18063
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
18064
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
18065
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
18066
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
18067
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
18068
+ * offers it as the default path; the fields the session fills are
18069
+ * marked `filledBySignIn` in the kind's settings schema.
18070
+ */
18071
+ signIn: object({ basePath: string().min(1) }).optional()
17850
18072
  }))
17851
18073
  });
17852
18074
  var ListInputSchema = object({
@@ -18951,7 +19173,8 @@ var CompositionProblemSchema = object({
18951
19173
  "native-capability-replace",
18952
19174
  "mixed-quantity",
18953
19175
  "helper-mismatch",
18954
- "member-constraint"
19176
+ "member-constraint",
19177
+ "retention"
18955
19178
  ]),
18956
19179
  path: string(),
18957
19180
  message: string()
@@ -19185,13 +19408,17 @@ Object.freeze({
19185
19408
  latchedAt: {
19186
19409
  kind: "fixed",
19187
19410
  result: ["number", "null"]
19411
+ },
19412
+ schmitt: {
19413
+ kind: "fixed",
19414
+ result: ["boolean", "null"]
19188
19415
  }
19189
19416
  });
19190
19417
  /**
19191
19418
  * One catalog row per base reading a group helper can build (D944 §3).
19192
19419
  * Data, not a form default written into the block.
19193
19420
  */
19194
- var NOW = {
19421
+ var NOW$2 = {
19195
19422
  fieldPath: "lastFetchedAt",
19196
19423
  expr: "now"
19197
19424
  };
@@ -19205,6 +19432,8 @@ function numeric(input) {
19205
19432
  family: "numeric",
19206
19433
  deviceType: DeviceType.Sensor,
19207
19434
  memberDeviceType: DeviceType.Sensor,
19435
+ memberDeviceTypes: [DeviceType.Sensor],
19436
+ activeWhen: void 0,
19208
19437
  commandCaps: [],
19209
19438
  baseUnit: baseUnitOf(input.quantity),
19210
19439
  aggregates: NUMERIC_AGGREGATES,
@@ -19220,6 +19449,8 @@ function binary(input) {
19220
19449
  family: "binary",
19221
19450
  deviceType: DeviceType.Sensor,
19222
19451
  memberDeviceType: DeviceType.Sensor,
19452
+ memberDeviceTypes: [DeviceType.Sensor],
19453
+ activeWhen: void 0,
19223
19454
  commandCaps: [],
19224
19455
  quantity: "none",
19225
19456
  baseUnit: void 0,
@@ -19237,7 +19468,7 @@ var GROUP_TEMPLATES = [
19237
19468
  quantity: "temperature",
19238
19469
  defaultAggregate: "mean",
19239
19470
  empty: "null",
19240
- companions: [NOW]
19471
+ companions: [NOW$2]
19241
19472
  }),
19242
19473
  numeric({
19243
19474
  id: "humidity",
@@ -19248,7 +19479,7 @@ var GROUP_TEMPLATES = [
19248
19479
  quantity: "humidity",
19249
19480
  defaultAggregate: "mean",
19250
19481
  empty: "null",
19251
- companions: [NOW]
19482
+ companions: [NOW$2]
19252
19483
  }),
19253
19484
  numeric({
19254
19485
  id: "pressure",
@@ -19259,7 +19490,7 @@ var GROUP_TEMPLATES = [
19259
19490
  quantity: "pressure",
19260
19491
  defaultAggregate: "mean",
19261
19492
  empty: "null",
19262
- companions: [NOW]
19493
+ companions: [NOW$2]
19263
19494
  }),
19264
19495
  numeric({
19265
19496
  id: "illuminance",
@@ -19270,7 +19501,7 @@ var GROUP_TEMPLATES = [
19270
19501
  quantity: "illuminance",
19271
19502
  defaultAggregate: "mean",
19272
19503
  empty: "null",
19273
- companions: [NOW]
19504
+ companions: [NOW$2]
19274
19505
  }),
19275
19506
  numeric({
19276
19507
  id: "power",
@@ -19281,7 +19512,7 @@ var GROUP_TEMPLATES = [
19281
19512
  quantity: "power",
19282
19513
  defaultAggregate: "sum",
19283
19514
  empty: "omit",
19284
- companions: [NOW]
19515
+ companions: [NOW$2]
19285
19516
  }),
19286
19517
  numeric({
19287
19518
  id: "energy",
@@ -19292,7 +19523,7 @@ var GROUP_TEMPLATES = [
19292
19523
  quantity: "energy",
19293
19524
  defaultAggregate: "sum",
19294
19525
  empty: "omit",
19295
- companions: [NOW]
19526
+ companions: [NOW$2]
19296
19527
  }),
19297
19528
  numeric({
19298
19529
  id: "battery",
@@ -19327,7 +19558,7 @@ var GROUP_TEMPLATES = [
19327
19558
  quantity: "device",
19328
19559
  defaultAggregate: "mean",
19329
19560
  empty: "null",
19330
- companions: [NOW]
19561
+ companions: [NOW$2]
19331
19562
  }),
19332
19563
  binary({
19333
19564
  id: "motion",
@@ -19421,6 +19652,11 @@ var GROUP_TEMPLATES = [
19421
19652
  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.",
19422
19653
  deviceType: DeviceType.Switch,
19423
19654
  outputCap: "switch",
19655
+ outputField: "on",
19656
+ member: {
19657
+ cap: "switch",
19658
+ fieldPath: "on"
19659
+ },
19424
19660
  commandCaps: ["switch"]
19425
19661
  }),
19426
19662
  actuator({
@@ -19428,34 +19664,136 @@ var GROUP_TEMPLATES = [
19428
19664
  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.",
19429
19665
  deviceType: DeviceType.Light,
19430
19666
  outputCap: "switch",
19667
+ outputField: "on",
19668
+ member: {
19669
+ cap: "switch",
19670
+ fieldPath: "on"
19671
+ },
19431
19672
  commandCaps: [
19432
19673
  "switch",
19433
19674
  "brightness",
19434
19675
  "color"
19435
19676
  ]
19677
+ }),
19678
+ actuator({
19679
+ id: "fan",
19680
+ 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.",
19681
+ deviceType: DeviceType.Fan,
19682
+ outputCap: "switch",
19683
+ outputField: "on",
19684
+ member: {
19685
+ cap: "switch",
19686
+ fieldPath: "on"
19687
+ },
19688
+ commandCaps: ["switch", "fan-control"]
19689
+ }),
19690
+ actuator({
19691
+ id: "cover",
19692
+ 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.",
19693
+ deviceType: DeviceType.Cover,
19694
+ outputCap: "group-active",
19695
+ outputField: "active",
19696
+ member: {
19697
+ cap: "cover",
19698
+ fieldPath: "state"
19699
+ },
19700
+ activeWhen: [
19701
+ "open",
19702
+ "opening",
19703
+ "stopped"
19704
+ ],
19705
+ commandCaps: ["cover"]
19706
+ }),
19707
+ actuator({
19708
+ id: "valve",
19709
+ 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.",
19710
+ deviceType: DeviceType.Valve,
19711
+ outputCap: "group-active",
19712
+ outputField: "active",
19713
+ member: {
19714
+ cap: "valve",
19715
+ fieldPath: "state"
19716
+ },
19717
+ activeWhen: [
19718
+ "open",
19719
+ "opening",
19720
+ "stopped"
19721
+ ],
19722
+ commandCaps: ["valve"]
19723
+ }),
19724
+ actuator({
19725
+ id: "lock",
19726
+ 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.",
19727
+ deviceType: DeviceType.Lock,
19728
+ outputCap: "group-active",
19729
+ outputField: "active",
19730
+ member: {
19731
+ cap: "lock-control",
19732
+ fieldPath: "state"
19733
+ },
19734
+ activeWhen: ["locked"],
19735
+ commandCaps: ["lock-control"]
19736
+ }),
19737
+ actuator({
19738
+ id: "media-player",
19739
+ 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.",
19740
+ deviceType: DeviceType.MediaPlayer,
19741
+ outputCap: "group-active",
19742
+ outputField: "active",
19743
+ member: {
19744
+ cap: "media-player",
19745
+ fieldPath: "state"
19746
+ },
19747
+ activeWhen: ["playing", "buffering"],
19748
+ commandCaps: ["media-player"]
19749
+ }),
19750
+ actuator({
19751
+ id: "climate",
19752
+ 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.",
19753
+ deviceType: DeviceType.Thermostat,
19754
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
19755
+ outputCap: "group-active",
19756
+ outputField: "active",
19757
+ member: {
19758
+ cap: "climate-control",
19759
+ fieldPath: "mode"
19760
+ },
19761
+ activeWhen: [
19762
+ "heat",
19763
+ "cool",
19764
+ "auto",
19765
+ "heat_cool",
19766
+ "fan_only",
19767
+ "dry"
19768
+ ],
19769
+ commandCaps: ["climate-control"]
19436
19770
  })
19437
19771
  ];
19438
19772
  function actuator(input) {
19439
19773
  return {
19440
- ...input,
19774
+ id: input.id,
19775
+ summary: input.summary,
19776
+ deviceType: input.deviceType,
19777
+ outputCap: input.outputCap,
19778
+ outputField: input.outputField,
19779
+ commandCaps: input.commandCaps,
19441
19780
  family: "actuator",
19442
19781
  role: void 0,
19443
19782
  memberDeviceType: input.deviceType,
19444
- outputField: "on",
19783
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
19784
+ activeWhen: input.activeWhen,
19445
19785
  quantity: "none",
19446
19786
  baseUnit: void 0,
19447
19787
  aggregates: BINARY_AGGREGATES,
19448
19788
  defaultAggregate: "any",
19449
19789
  empty: "offline",
19450
- accept: [{
19451
- cap: "switch",
19452
- fieldPath: "on"
19453
- }],
19790
+ accept: [input.member],
19454
19791
  companions: [CHANGED]
19455
19792
  };
19456
19793
  }
19457
19794
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
19458
19795
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
19796
+ DeviceType.Camera;
19459
19797
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19460
19798
  /**
19461
19799
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -44775,7 +45113,7 @@ var NetworkAccessStatusSchema = object({
44775
45113
  });
44776
45114
  /**
44777
45115
  * Optional, richer endpoint shape returned by providers that expose
44778
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
45116
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44779
45117
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44780
45118
  * the originating provider config (mode + sourcePort) so the
44781
45119
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -47462,7 +47800,20 @@ var NcRuleInputSchema = object({
47462
47800
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47463
47801
  * path does not run Zod defaults.
47464
47802
  */
47465
- groupingSec: number().int().min(5).max(60).optional()
47803
+ groupingSec: number().int().min(5).max(60).optional(),
47804
+ /**
47805
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
47806
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
47807
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
47808
+ * same mark the Library writes, and records whose notification it was and
47809
+ * when. A channel with no buttons gets the same two choices as links.
47810
+ *
47811
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
47812
+ * cap path): absent and `false` both mean off, which is what every rule
47813
+ * authored before this field meant. An editor turns it off by writing
47814
+ * `false`, so it needs no `clear` key.
47815
+ */
47816
+ askVerdict: boolean().optional()
47466
47817
  });
47467
47818
  /**
47468
47819
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -58182,7 +58533,8 @@ var BATTERY_DEVICE_TYPES = [
58182
58533
  DeviceType.Switch,
58183
58534
  DeviceType.PetFeeder,
58184
58535
  DeviceType.Container,
58185
- DeviceType.Thermostat
58536
+ DeviceType.Thermostat,
58537
+ DeviceType.EventEmitter
58186
58538
  ];
58187
58539
  /** Generic Customize target for the percentage reading (D936). */
58188
58540
  var batteryCustomize = {
@@ -61042,7 +61394,17 @@ var groupCommandsCapability = {
61042
61394
  scope: "device",
61043
61395
  deviceNative: true,
61044
61396
  mode: "singleton",
61045
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
61397
+ deviceTypes: [
61398
+ DeviceType.Switch,
61399
+ DeviceType.Light,
61400
+ DeviceType.Fan,
61401
+ DeviceType.Cover,
61402
+ DeviceType.Valve,
61403
+ DeviceType.Lock,
61404
+ DeviceType.MediaPlayer,
61405
+ DeviceType.Thermostat,
61406
+ DeviceType.Climate
61407
+ ],
61046
61408
  methods: {},
61047
61409
  status: {
61048
61410
  schema: GroupCommandsSchema,
@@ -61060,6 +61422,49 @@ var groupCommandsCapability = {
61060
61422
  history: "none"
61061
61423
  };
61062
61424
  /**
61425
+ * Whether a helper actuator group counts as active (D944): at least one
61426
+ * included member is, or every included member is. What "active" means is the
61427
+ * type's: a cover is open, a lock is locked, a media player is playing, a
61428
+ * climate device is not off. A group of lights, switches and fans reports
61429
+ * `switch.on` instead; this cap is for the types whose own state is not a
61430
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
61431
+ * empty included set takes the device offline, never `active: false`.
61432
+ */
61433
+ var GroupActiveSchema = object({
61434
+ active: boolean().meta({ label: "Active" }),
61435
+ /** Ms epoch when the slice was last updated. */
61436
+ lastChangedAt: number()
61437
+ });
61438
+ var groupActiveCapability = {
61439
+ name: "group-active",
61440
+ scope: "device",
61441
+ deviceNative: true,
61442
+ mode: "singleton",
61443
+ deviceTypes: [
61444
+ DeviceType.Cover,
61445
+ DeviceType.Valve,
61446
+ DeviceType.Lock,
61447
+ DeviceType.MediaPlayer,
61448
+ DeviceType.Climate,
61449
+ DeviceType.Thermostat
61450
+ ],
61451
+ methods: {},
61452
+ status: {
61453
+ schema: GroupActiveSchema,
61454
+ kind: "push"
61455
+ },
61456
+ runtimeState: GroupActiveSchema,
61457
+ /**
61458
+ * Runtime-state durability: **session** — recomputed from the members on the
61459
+ * next source event; a restored value would outlive the members it named.
61460
+ *
61461
+ * See `RuntimeStateDurability`. Enforced by
61462
+ * `scripts/check-runtime-state-durability.ts`.
61463
+ */
61464
+ durability: "session",
61465
+ history: "none"
61466
+ };
61467
+ /**
61063
61468
  * How many members of a helper group answered (D944). Counts are integers,
61064
61469
  * not measurements: zero included is a real count, never a stand-in reading.
61065
61470
  * The reading itself stays on the output capability. History is none — the
@@ -61090,7 +61495,14 @@ var groupStatusCapability = {
61090
61495
  deviceTypes: [
61091
61496
  DeviceType.Sensor,
61092
61497
  DeviceType.Switch,
61093
- DeviceType.Light
61498
+ DeviceType.Light,
61499
+ DeviceType.Fan,
61500
+ DeviceType.Cover,
61501
+ DeviceType.Valve,
61502
+ DeviceType.Lock,
61503
+ DeviceType.MediaPlayer,
61504
+ DeviceType.Thermostat,
61505
+ DeviceType.Climate
61094
61506
  ],
61095
61507
  methods: {},
61096
61508
  status: {
@@ -62363,7 +62775,14 @@ var lockControlCapability = {
62363
62775
  }),
62364
62776
  unlock: method(object({
62365
62777
  deviceId: number().int().nonnegative(),
62366
- code: string().min(1).optional()
62778
+ code: string().min(1).optional(),
62779
+ /**
62780
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62781
+ * with one call is refused unless the caller says it was confirmed. A
62782
+ * single lock ignores it. The refusal lives in the callee, so no
62783
+ * automation or API caller reaches the doors by skipping a dialog.
62784
+ */
62785
+ confirm: boolean().optional()
62367
62786
  }), _void(), {
62368
62787
  kind: "mutation",
62369
62788
  auth: "protected"
@@ -62400,6 +62819,44 @@ var lockControlCapability = {
62400
62819
  volatileStateFields: ["lastChangedAt"]
62401
62820
  };
62402
62821
  /**
62822
+ * A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
62823
+ * so dashboards keep one series; every other instantaneous reading lands here.
62824
+ * `value` is the time integral in the source quantity's accumulated base
62825
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
62826
+ */
62827
+ var MeterStatusSchema = object({
62828
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
62829
+ lastFetchedAt: number()
62830
+ });
62831
+ var meterCapability = {
62832
+ name: "meter",
62833
+ scope: "device",
62834
+ deviceNative: true,
62835
+ mode: "singleton",
62836
+ deviceTypes: [DeviceType.Sensor],
62837
+ methods: {},
62838
+ status: {
62839
+ schema: MeterStatusSchema,
62840
+ kind: "push"
62841
+ },
62842
+ runtimeState: MeterStatusSchema,
62843
+ /**
62844
+ * Runtime-state durability: **restored** — the total is a cache of the
62845
+ * history integral. A restart shows it only until the history pass
62846
+ * recomputes the open period; it is not an in-memory buffer.
62847
+ *
62848
+ * See `RuntimeStateDurability`. Enforced by
62849
+ * `scripts/check-runtime-state-durability.ts`.
62850
+ */
62851
+ durability: "restored",
62852
+ history: { fields: { value: {
62853
+ kind: "counter",
62854
+ deadband: .001,
62855
+ minIntervalMs: 6e4
62856
+ } } },
62857
+ volatileStateFields: ["lastFetchedAt"]
62858
+ };
62859
+ /**
62403
62860
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
62404
62861
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
62405
62862
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -64911,9 +65368,9 @@ var PowerMeterStatusSchema = object({
64911
65368
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64912
65369
  kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64913
65370
  /** Voltage in volts. */
64914
- volts: number().optional().meta(readingMeta("voltage")),
65371
+ volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64915
65372
  /** Current in amperes. */
64916
- amps: number().optional().meta(readingMeta("current")),
65373
+ amps: number().optional().meta(readingMeta("current", { label: "Current" })),
64917
65374
  /** Apparent power in volt-amperes. */
64918
65375
  va: number().optional().meta(readingMeta("apparent-power")),
64919
65376
  /** Power factor (0..1) if reported. */
@@ -64921,12 +65378,74 @@ var PowerMeterStatusSchema = object({
64921
65378
  /** Ms epoch when the slice was last updated. */
64922
65379
  lastFetchedAt: number()
64923
65380
  });
65381
+ /**
65382
+ * Loads that draw metered mains power (D951). The reading is a feature of the
65383
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
65384
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
65385
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
65386
+ * camera and the like are left out: their draw is not what anyone meters.
65387
+ */
65388
+ var POWER_METER_DEVICE_TYPES = [
65389
+ DeviceType.Sensor,
65390
+ DeviceType.Switch,
65391
+ DeviceType.Light,
65392
+ DeviceType.Fan,
65393
+ DeviceType.Climate,
65394
+ DeviceType.Thermostat,
65395
+ DeviceType.Humidifier,
65396
+ DeviceType.WaterHeater,
65397
+ DeviceType.MediaPlayer,
65398
+ DeviceType.Washer,
65399
+ DeviceType.Dryer,
65400
+ DeviceType.Dishwasher,
65401
+ DeviceType.Vacuum,
65402
+ DeviceType.LawnMower,
65403
+ DeviceType.PetFeeder
65404
+ ];
65405
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
65406
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
65407
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
65408
+ function powerTarget(id, unit) {
65409
+ return {
65410
+ id,
65411
+ cardinality: "scalar",
65412
+ vocabulary: {
65413
+ cap: "power-meter",
65414
+ stateField: id
65415
+ },
65416
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
65417
+ apply: "add",
65418
+ path: id,
65419
+ sources: [{
65420
+ kind: "numeric",
65421
+ requiredUnit: unit,
65422
+ transforms: [{ kind: "identity" }]
65423
+ }],
65424
+ companions: [{
65425
+ kind: "clock",
65426
+ path: "lastFetchedAt",
65427
+ when: "always"
65428
+ }],
65429
+ sourceReport: { status: "assignment-status" },
65430
+ advancedExpression: true
65431
+ };
65432
+ }
65433
+ /** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
65434
+ var powerMeterCustomize = {
65435
+ version: 1,
65436
+ targets: [
65437
+ powerTarget("watts", "W"),
65438
+ powerTarget("kwhTotal", "kWh"),
65439
+ powerTarget("volts", "V"),
65440
+ powerTarget("amps", "A")
65441
+ ]
65442
+ };
64924
65443
  var powerMeterCapability = {
64925
65444
  name: "power-meter",
64926
65445
  scope: "device",
64927
65446
  deviceNative: true,
64928
65447
  mode: "singleton",
64929
- deviceTypes: [DeviceType.Sensor],
65448
+ deviceTypes: POWER_METER_DEVICE_TYPES,
64930
65449
  methods: {},
64931
65450
  status: {
64932
65451
  schema: PowerMeterStatusSchema,
@@ -64983,7 +65502,8 @@ var powerMeterCapability = {
64983
65502
  } },
64984
65503
  /** Clock fields: written, but excluded from the compare that decides
64985
65504
  * whether persisting is worth a SQLite commit. */
64986
- volatileStateFields: ["lastFetchedAt"]
65505
+ volatileStateFields: ["lastFetchedAt"],
65506
+ customize: powerMeterCustomize
64987
65507
  };
64988
65508
  /**
64989
65509
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -65664,6 +66184,8 @@ var runningCustomize = {
65664
66184
  stateField: "running"
65665
66185
  },
65666
66186
  deviceTypes: [
66187
+ DeviceType.Washer,
66188
+ DeviceType.Dryer,
65667
66189
  DeviceType.Dishwasher,
65668
66190
  DeviceType.Vacuum,
65669
66191
  DeviceType.LawnMower,
@@ -68153,7 +68675,8 @@ var switchCapability = {
68153
68675
  deviceTypes: [
68154
68676
  DeviceType.Switch,
68155
68677
  DeviceType.Siren,
68156
- DeviceType.Light
68678
+ DeviceType.Light,
68679
+ DeviceType.Fan
68157
68680
  ],
68158
68681
  methods: { setState: method(object({
68159
68682
  deviceId: number(),
@@ -70366,8 +70889,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
70366
70889
  kind: "mutation",
70367
70890
  auth: "admin"
70368
70891
  });
70892
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
70369
70893
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
70370
- 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;
70894
+ 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;
70371
70895
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
70372
70896
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
70373
70897
  new Set(CAP_NAMES);
@@ -70696,6 +71220,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70696
71220
  featureProbe: featureProbeCapability,
70697
71221
  flood: floodCapability,
70698
71222
  gas: gasCapability,
71223
+ groupActive: groupActiveCapability,
70699
71224
  groupCommands: groupCommandsCapability,
70700
71225
  groupStatus: groupStatusCapability,
70701
71226
  heatingValve: heatingValveCapability,
@@ -70707,6 +71232,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70707
71232
  lawnMowerControl: lawnMowerControlCapability,
70708
71233
  lockControl: lockControlCapability,
70709
71234
  mediaPlayer: mediaPlayerCapability,
71235
+ meter: meterCapability,
70710
71236
  motion: motionCapability,
70711
71237
  motionTrigger: motionTriggerCapability,
70712
71238
  motionZones: motionZonesCapability,
@@ -71966,7 +72492,7 @@ var BaseDeviceProvider = class extends BaseAddon {
71966
72492
  return toDeviceSummary(device, this.addonId);
71967
72493
  }
71968
72494
  };
71969
- 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;
72495
+ 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;
71970
72496
  new Set(Object.values(DeviceType));
71971
72497
  Object.freeze({
71972
72498
  "accessories.getStatus": {
@@ -75189,6 +75715,13 @@ Object.freeze({
75189
75715
  access: "view",
75190
75716
  role: "viewer"
75191
75717
  },
75718
+ "groupActive.getStatus": {
75719
+ capName: "group-active",
75720
+ capScope: "device",
75721
+ addonId: null,
75722
+ access: "view",
75723
+ role: "viewer"
75724
+ },
75192
75725
  "groupCommands.getStatus": {
75193
75726
  capName: "group-commands",
75194
75727
  capScope: "device",
@@ -76155,6 +76688,13 @@ Object.freeze({
76155
76688
  access: "create",
76156
76689
  role: "admin"
76157
76690
  },
76691
+ "meter.getStatus": {
76692
+ capName: "meter",
76693
+ capScope: "device",
76694
+ addonId: null,
76695
+ access: "view",
76696
+ role: "viewer"
76697
+ },
76158
76698
  "metricsProvider.collectSnapshot": {
76159
76699
  capName: "metrics-provider",
76160
76700
  capScope: "system",
@@ -84253,7 +84793,39 @@ var APPLIANCE_CAP_VOCABULARY = {
84253
84793
  phrase: t("When {device} finishes", "Quando {device} finisce"),
84254
84794
  icon: "square"
84255
84795
  }
84256
- }
84796
+ },
84797
+ overrides: { byType: {
84798
+ [DeviceType.Washer]: {
84799
+ state: { running: {
84800
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
84801
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
84802
+ } },
84803
+ events: {
84804
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
84805
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
84806
+ }
84807
+ },
84808
+ [DeviceType.Dryer]: {
84809
+ state: { running: {
84810
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
84811
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
84812
+ } },
84813
+ events: {
84814
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
84815
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
84816
+ }
84817
+ },
84818
+ [DeviceType.Dishwasher]: {
84819
+ state: { running: {
84820
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
84821
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
84822
+ } },
84823
+ events: {
84824
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
84825
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
84826
+ }
84827
+ }
84828
+ } }
84257
84829
  },
84258
84830
  update: {
84259
84831
  scope: "device",
@@ -85025,6 +85597,10 @@ var SENSOR_CAP_VOCABULARY = {
85025
85597
  icon: "flame"
85026
85598
  }]
85027
85599
  },
85600
+ "group-active": {
85601
+ scope: "device",
85602
+ noun: t("Group state", "Stato del gruppo")
85603
+ },
85028
85604
  "group-commands": {
85029
85605
  scope: "device",
85030
85606
  noun: t("Group command", "Comando di gruppo")
@@ -85081,6 +85657,17 @@ var SENSOR_CAP_VOCABULARY = {
85081
85657
  }],
85082
85658
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
85083
85659
  },
85660
+ meter: {
85661
+ scope: "device",
85662
+ noun: t("Meter", "Contatore"),
85663
+ state: [{
85664
+ kind: "numeric",
85665
+ field: "value",
85666
+ quantity: t("the total", "il totale"),
85667
+ label: t("Total", "Totale"),
85668
+ icon: "gauge"
85669
+ }]
85670
+ },
85084
85671
  "numeric-sensor": {
85085
85672
  scope: "device",
85086
85673
  noun: t("Sensor", "Sensore"),
@@ -85553,8 +86140,47 @@ function projectAction(cap, action) {
85553
86140
  };
85554
86141
  }
85555
86142
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
85556
- 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;
86143
+ 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;
85557
86144
  DeviceFeature.BatteryOperated;
86145
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
86146
+ 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.";
86147
+ 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.";
86148
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
86149
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
86150
+ 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.";
86151
+ function withheld(reason) {
86152
+ return {
86153
+ [DeviceType.Washer]: {
86154
+ exported: false,
86155
+ reason
86156
+ },
86157
+ [DeviceType.Dryer]: {
86158
+ exported: false,
86159
+ reason
86160
+ },
86161
+ [DeviceType.Dishwasher]: {
86162
+ exported: false,
86163
+ reason
86164
+ }
86165
+ };
86166
+ }
86167
+ function offered(received) {
86168
+ return {
86169
+ [DeviceType.Washer]: {
86170
+ exported: true,
86171
+ as: received
86172
+ },
86173
+ [DeviceType.Dryer]: {
86174
+ exported: true,
86175
+ as: received
86176
+ },
86177
+ [DeviceType.Dishwasher]: {
86178
+ exported: true,
86179
+ as: received
86180
+ }
86181
+ };
86182
+ }
86183
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
85558
86184
  Object.freeze({
85559
86185
  "accessories.getStatus": [{
85560
86186
  name: "deviceId",
@@ -86521,6 +87147,11 @@ Object.freeze({
86521
87147
  form: "single",
86522
87148
  optional: false
86523
87149
  }],
87150
+ "groupActive.getStatus": [{
87151
+ name: "deviceId",
87152
+ form: "single",
87153
+ optional: false
87154
+ }],
86524
87155
  "groupCommands.getStatus": [{
86525
87156
  name: "deviceId",
86526
87157
  form: "single",
@@ -86731,6 +87362,11 @@ Object.freeze({
86731
87362
  form: "single",
86732
87363
  optional: false
86733
87364
  }],
87365
+ "meter.getStatus": [{
87366
+ name: "deviceId",
87367
+ form: "single",
87368
+ optional: false
87369
+ }],
86734
87370
  "motion.getStatus": [{
86735
87371
  name: "deviceId",
86736
87372
  form: "single",
@@ -88338,7 +88974,6 @@ Object.freeze({
88338
88974
  "network-access": "ingress",
88339
88975
  "smtp-provider": "email"
88340
88976
  });
88341
- DeviceType.Camera;
88342
88977
  /**
88343
88978
  * A saved Customize assignment (D936). The schema is the type: callers use
88344
88979
  * `z.infer`, they do not restate the shape.