@camstack/addon-provider-amcrest 0.2.161 → 0.2.162

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.mjs CHANGED
@@ -5457,7 +5457,7 @@ var ZodIssueCode = {
5457
5457
  var ZodFirstPartyTypeKind;
5458
5458
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5459
5459
  //#endregion
5460
- //#region ../types/dist/sleep-bykKrTgu.mjs
5460
+ //#region ../types/dist/sleep--jXS9hWo.mjs
5461
5461
  /**
5462
5462
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5463
5463
  * window to float samples (D455).
@@ -8650,6 +8650,7 @@ var CAP_NAMES = Object.freeze([
8650
8650
  "filesystem-browse",
8651
8651
  "flood",
8652
8652
  "gas",
8653
+ "group-active",
8653
8654
  "group-commands",
8654
8655
  "group-status",
8655
8656
  "heating-valve",
@@ -8672,6 +8673,7 @@ var CAP_NAMES = Object.freeze([
8672
8673
  "login-method",
8673
8674
  "media-player",
8674
8675
  "mesh-network",
8676
+ "meter",
8675
8677
  "metrics-provider",
8676
8678
  "model-convert",
8677
8679
  "model-distributor",
@@ -9470,6 +9472,10 @@ Object.freeze({
9470
9472
  "path": "detected",
9471
9473
  "valueType": "boolean"
9472
9474
  }],
9475
+ "group-active": [{
9476
+ "path": "active",
9477
+ "valueType": "boolean"
9478
+ }],
9473
9479
  "group-status": [
9474
9480
  {
9475
9481
  "path": "included",
@@ -9729,6 +9735,10 @@ Object.freeze({
9729
9735
  ]
9730
9736
  }
9731
9737
  ],
9738
+ "meter": [{
9739
+ "path": "value",
9740
+ "valueType": "number"
9741
+ }],
9732
9742
  "motion": [
9733
9743
  {
9734
9744
  "path": "detected",
@@ -10486,7 +10496,7 @@ function userError(code, params, englishFallback) {
10486
10496
  return new CamstackError(code, params, englishFallback);
10487
10497
  }
10488
10498
  //#endregion
10489
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10499
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10490
10500
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10491
10501
  DeviceType["Camera"] = "camera";
10492
10502
  DeviceType["Hub"] = "hub";
@@ -10580,8 +10590,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10580
10590
  DeviceType["PetFeeder"] = "pet-feeder";
10581
10591
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10582
10592
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10583
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10593
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10594
+ * A plug-built dishwasher carries running-state and power instead. */
10584
10595
  DeviceType["Dishwasher"] = "dishwasher";
10596
+ /** Washing machine. A plug-built one is running while its power stays
10597
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10598
+ DeviceType["Washer"] = "washer";
10599
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10600
+ DeviceType["Dryer"] = "dryer";
10585
10601
  return DeviceType;
10586
10602
  }({});
10587
10603
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -10829,7 +10845,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
10829
10845
  return DeviceRole;
10830
10846
  }({});
10831
10847
  //#endregion
10832
- //#region ../types/dist/composition-OhpuespA.mjs
10848
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
10833
10849
  /**
10834
10850
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10835
10851
  *
@@ -11144,6 +11160,13 @@ var table = {
11144
11160
  apply: () => {
11145
11161
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11146
11162
  }
11163
+ },
11164
+ schmitt: {
11165
+ minArgs: 3,
11166
+ maxArgs: 5,
11167
+ apply: () => {
11168
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11169
+ }
11147
11170
  }
11148
11171
  };
11149
11172
  Object.freeze(Object.assign(Object.create(null), table));
@@ -11585,6 +11608,99 @@ function validateExpressionSource(src) {
11585
11608
  return null;
11586
11609
  }
11587
11610
  /**
11611
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11612
+ *
11613
+ * Standby of the control board is under 5 W; a motor or a heater is far
11614
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11615
+ * a cycle, so running starts only after 30 s above the line.
11616
+ *
11617
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11618
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11619
+ * report "finished" in the middle of the program, so the stop dwell is
11620
+ * 10 minutes on every type. The number is stored per type so one machine
11621
+ * can move later without moving the others.
11622
+ */
11623
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11624
+ var APPLIANCE_KINDS = [
11625
+ "washer",
11626
+ "dryer",
11627
+ "dishwasher"
11628
+ ];
11629
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11630
+ /**
11631
+ * Statistics and integrals over a history read (D944).
11632
+ *
11633
+ * Pure: the same points always give the same answer, so a restart recomputes
11634
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11635
+ * never the mean of the samples. A hole with no held value is not integrated
11636
+ * as zero. A truncated read is not published.
11637
+ */
11638
+ var STATISTIC_STATS = [
11639
+ "min",
11640
+ "max",
11641
+ "mean",
11642
+ "sum",
11643
+ "count",
11644
+ "change"
11645
+ ];
11646
+ /**
11647
+ * The range a statistics or integral helper asks history for (D944).
11648
+ *
11649
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11650
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11651
+ * is strictly longer than the longest that range can be. Forever qualifies.
11652
+ * Equal to a tier's retention is a refusal, not a short answer.
11653
+ */
11654
+ var MINUTE_MS = 6e4;
11655
+ var HOUR_MS = 60 * MINUTE_MS;
11656
+ var DAY_MS = 24 * HOUR_MS;
11657
+ var ROLLING_SPANS = [
11658
+ "1h",
11659
+ "24h",
11660
+ "2d",
11661
+ "7d",
11662
+ "30d",
11663
+ "custom"
11664
+ ];
11665
+ var CALENDAR_PERIODS = [
11666
+ "today",
11667
+ "yesterday",
11668
+ "this-week",
11669
+ "this-month"
11670
+ ];
11671
+ var RESET_PERIODS = [
11672
+ "never",
11673
+ "daily",
11674
+ "weekly",
11675
+ "monthly"
11676
+ ];
11677
+ 5 * MINUTE_MS;
11678
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
11679
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
11680
+ var rollingWindowObject = object({
11681
+ kind: literal("rolling"),
11682
+ span: _enum(ROLLING_SPANS),
11683
+ customMinutes: number().int().min(5).optional()
11684
+ }).check((payload) => {
11685
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
11686
+ code: "custom",
11687
+ message: "a custom range needs a duration in minutes",
11688
+ path: ["customMinutes"],
11689
+ input: payload.value
11690
+ });
11691
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
11692
+ code: "custom",
11693
+ message: "only a custom range carries a duration",
11694
+ path: ["customMinutes"],
11695
+ input: payload.value
11696
+ });
11697
+ });
11698
+ var RollingWindowSchema = rollingWindowObject;
11699
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
11700
+ kind: literal("calendar"),
11701
+ period: _enum(CALENDAR_PERIODS)
11702
+ })]);
11703
+ /**
11588
11704
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11589
11705
  * id. Split out so helper schemas can name it without a cycle through
11590
11706
  * `composition.ts` (D659).
@@ -11610,7 +11726,13 @@ var GROUP_TEMPLATE_IDS = [
11610
11726
  "occupancy",
11611
11727
  "binary",
11612
11728
  "switch",
11613
- "light"
11729
+ "light",
11730
+ "fan",
11731
+ "cover",
11732
+ "valve",
11733
+ "lock",
11734
+ "media-player",
11735
+ "climate"
11614
11736
  ];
11615
11737
  var NUMERIC_AGGREGATES = [
11616
11738
  "mean",
@@ -11640,14 +11762,73 @@ var HelperMemberSchema = object({
11640
11762
  cap: string().min(1),
11641
11763
  fieldPath: string().min(1)
11642
11764
  });
11643
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11644
- kind: literal("group"),
11645
- templateId: GroupTemplateIdSchema,
11646
- aggregate: AggregateReducerSchema,
11647
- members: array(HelperMemberSchema).min(1).max(64),
11648
- /** Actuator groups only. Readings leave this unset. */
11649
- active: ActiveCriterionSchema.optional()
11650
- })]);
11765
+ var HelperSpecSchema = discriminatedUnion("kind", [
11766
+ object({
11767
+ kind: literal("group"),
11768
+ templateId: GroupTemplateIdSchema,
11769
+ aggregate: AggregateReducerSchema,
11770
+ members: array(HelperMemberSchema).min(1).max(64),
11771
+ /** Actuator groups only. Readings leave this unset. */
11772
+ active: ActiveCriterionSchema.optional()
11773
+ }),
11774
+ object({
11775
+ kind: literal("appliance"),
11776
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
11777
+ appliance: _enum(APPLIANCE_KINDS),
11778
+ thresholdWatts: number().positive(),
11779
+ startDwellMs: number().int().nonnegative(),
11780
+ stopDwellMs: number().int().nonnegative(),
11781
+ member: HelperMemberSchema
11782
+ }),
11783
+ object({
11784
+ kind: literal("statistic"),
11785
+ stat: _enum(STATISTIC_STATS),
11786
+ window: StatisticWindowSchema,
11787
+ source: HelperMemberSchema
11788
+ }),
11789
+ object({
11790
+ kind: literal("integral"),
11791
+ reset: _enum(RESET_PERIODS),
11792
+ output: _enum(["energy", "meter"]),
11793
+ source: HelperMemberSchema
11794
+ }),
11795
+ object({
11796
+ kind: literal("threshold"),
11797
+ source: HelperMemberSchema,
11798
+ high: number(),
11799
+ low: number(),
11800
+ onForMs: number().int().nonnegative(),
11801
+ offForMs: number().int().nonnegative()
11802
+ }),
11803
+ object({
11804
+ kind: literal("arithmetic"),
11805
+ op: _enum([
11806
+ "sum",
11807
+ "difference",
11808
+ "ratio"
11809
+ ]),
11810
+ inputs: array(HelperMemberSchema).min(2).max(64)
11811
+ }),
11812
+ object({
11813
+ kind: literal("expression"),
11814
+ expr: string().min(1),
11815
+ inputs: array(object({
11816
+ name: string().min(1),
11817
+ member: HelperMemberSchema
11818
+ })).min(1).max(64),
11819
+ output: _enum(["number", "boolean"])
11820
+ }),
11821
+ object({
11822
+ kind: literal("derivative"),
11823
+ window: RollingWindowSchema,
11824
+ per: _enum([
11825
+ "second",
11826
+ "minute",
11827
+ "hour"
11828
+ ]),
11829
+ source: HelperMemberSchema
11830
+ })
11831
+ ]);
11651
11832
  /** A composed device's stableId is this prefix plus the block id. */
11652
11833
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11653
11834
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -11708,6 +11889,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11708
11889
  "all"
11709
11890
  ]),
11710
11891
  reads: array(CompositionFieldReadSchema).min(1).max(64),
11892
+ /**
11893
+ * Set when the reads are an enum state, not a yes/no: the listed states
11894
+ * count as active (a cover is `open`). A state outside the list is inactive;
11895
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
11896
+ */
11897
+ activeWhen: array(string().min(1)).min(1).optional(),
11711
11898
  /** `value` is the reading; the others are the member counts. */
11712
11899
  publish: _enum([
11713
11900
  "value",
@@ -11721,6 +11908,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11721
11908
  "omit",
11722
11909
  "offline"
11723
11910
  ])
11911
+ }),
11912
+ object({
11913
+ kind: literal("series"),
11914
+ read: CompositionFieldReadSchema,
11915
+ /** `value` is the statistic or the integral. The others are the gap counts. */
11916
+ publish: _enum([
11917
+ "value",
11918
+ "included",
11919
+ "excluded",
11920
+ "total"
11921
+ ]),
11922
+ /**
11923
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
11924
+ * device unseeded until history answers, so a restart cannot publish 0.
11925
+ */
11926
+ empty: _enum([
11927
+ "null",
11928
+ "omit",
11929
+ "offline"
11930
+ ])
11724
11931
  })
11725
11932
  ]);
11726
11933
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -16918,7 +17125,22 @@ var BrokerProviderInfoSchema = object({
16918
17125
  */
16919
17126
  interactiveConnectPath: string().min(1).optional(),
16920
17127
  /** Relative hub path to disconnect the linked account (same popup pattern). */
16921
- interactiveDisconnectPath: string().min(1).optional()
17128
+ interactiveDisconnectPath: string().min(1).optional(),
17129
+ /**
17130
+ * The kind signs in through a server-held interactive session instead of
17131
+ * (or before) typed credentials. `basePath` is a hub-relative route set
17132
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
17133
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
17134
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
17135
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
17136
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
17137
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
17138
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
17139
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
17140
+ * offers it as the default path; the fields the session fills are
17141
+ * marked `filledBySignIn` in the kind's settings schema.
17142
+ */
17143
+ signIn: object({ basePath: string().min(1) }).optional()
16922
17144
  }))
16923
17145
  });
16924
17146
  var ListInputSchema = object({
@@ -18023,7 +18245,8 @@ var CompositionProblemSchema = object({
18023
18245
  "native-capability-replace",
18024
18246
  "mixed-quantity",
18025
18247
  "helper-mismatch",
18026
- "member-constraint"
18248
+ "member-constraint",
18249
+ "retention"
18027
18250
  ]),
18028
18251
  path: string(),
18029
18252
  message: string()
@@ -18257,13 +18480,17 @@ Object.freeze({
18257
18480
  latchedAt: {
18258
18481
  kind: "fixed",
18259
18482
  result: ["number", "null"]
18483
+ },
18484
+ schmitt: {
18485
+ kind: "fixed",
18486
+ result: ["boolean", "null"]
18260
18487
  }
18261
18488
  });
18262
18489
  /**
18263
18490
  * One catalog row per base reading a group helper can build (D944 §3).
18264
18491
  * Data, not a form default written into the block.
18265
18492
  */
18266
- var NOW = {
18493
+ var NOW$2 = {
18267
18494
  fieldPath: "lastFetchedAt",
18268
18495
  expr: "now"
18269
18496
  };
@@ -18277,6 +18504,8 @@ function numeric(input) {
18277
18504
  family: "numeric",
18278
18505
  deviceType: DeviceType.Sensor,
18279
18506
  memberDeviceType: DeviceType.Sensor,
18507
+ memberDeviceTypes: [DeviceType.Sensor],
18508
+ activeWhen: void 0,
18280
18509
  commandCaps: [],
18281
18510
  baseUnit: baseUnitOf(input.quantity),
18282
18511
  aggregates: NUMERIC_AGGREGATES,
@@ -18292,6 +18521,8 @@ function binary(input) {
18292
18521
  family: "binary",
18293
18522
  deviceType: DeviceType.Sensor,
18294
18523
  memberDeviceType: DeviceType.Sensor,
18524
+ memberDeviceTypes: [DeviceType.Sensor],
18525
+ activeWhen: void 0,
18295
18526
  commandCaps: [],
18296
18527
  quantity: "none",
18297
18528
  baseUnit: void 0,
@@ -18309,7 +18540,7 @@ var GROUP_TEMPLATES = [
18309
18540
  quantity: "temperature",
18310
18541
  defaultAggregate: "mean",
18311
18542
  empty: "null",
18312
- companions: [NOW]
18543
+ companions: [NOW$2]
18313
18544
  }),
18314
18545
  numeric({
18315
18546
  id: "humidity",
@@ -18320,7 +18551,7 @@ var GROUP_TEMPLATES = [
18320
18551
  quantity: "humidity",
18321
18552
  defaultAggregate: "mean",
18322
18553
  empty: "null",
18323
- companions: [NOW]
18554
+ companions: [NOW$2]
18324
18555
  }),
18325
18556
  numeric({
18326
18557
  id: "pressure",
@@ -18331,7 +18562,7 @@ var GROUP_TEMPLATES = [
18331
18562
  quantity: "pressure",
18332
18563
  defaultAggregate: "mean",
18333
18564
  empty: "null",
18334
- companions: [NOW]
18565
+ companions: [NOW$2]
18335
18566
  }),
18336
18567
  numeric({
18337
18568
  id: "illuminance",
@@ -18342,7 +18573,7 @@ var GROUP_TEMPLATES = [
18342
18573
  quantity: "illuminance",
18343
18574
  defaultAggregate: "mean",
18344
18575
  empty: "null",
18345
- companions: [NOW]
18576
+ companions: [NOW$2]
18346
18577
  }),
18347
18578
  numeric({
18348
18579
  id: "power",
@@ -18353,7 +18584,7 @@ var GROUP_TEMPLATES = [
18353
18584
  quantity: "power",
18354
18585
  defaultAggregate: "sum",
18355
18586
  empty: "omit",
18356
- companions: [NOW]
18587
+ companions: [NOW$2]
18357
18588
  }),
18358
18589
  numeric({
18359
18590
  id: "energy",
@@ -18364,7 +18595,7 @@ var GROUP_TEMPLATES = [
18364
18595
  quantity: "energy",
18365
18596
  defaultAggregate: "sum",
18366
18597
  empty: "omit",
18367
- companions: [NOW]
18598
+ companions: [NOW$2]
18368
18599
  }),
18369
18600
  numeric({
18370
18601
  id: "battery",
@@ -18399,7 +18630,7 @@ var GROUP_TEMPLATES = [
18399
18630
  quantity: "device",
18400
18631
  defaultAggregate: "mean",
18401
18632
  empty: "null",
18402
- companions: [NOW]
18633
+ companions: [NOW$2]
18403
18634
  }),
18404
18635
  binary({
18405
18636
  id: "motion",
@@ -18493,6 +18724,11 @@ var GROUP_TEMPLATES = [
18493
18724
  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.",
18494
18725
  deviceType: DeviceType.Switch,
18495
18726
  outputCap: "switch",
18727
+ outputField: "on",
18728
+ member: {
18729
+ cap: "switch",
18730
+ fieldPath: "on"
18731
+ },
18496
18732
  commandCaps: ["switch"]
18497
18733
  }),
18498
18734
  actuator({
@@ -18500,34 +18736,136 @@ var GROUP_TEMPLATES = [
18500
18736
  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.",
18501
18737
  deviceType: DeviceType.Light,
18502
18738
  outputCap: "switch",
18739
+ outputField: "on",
18740
+ member: {
18741
+ cap: "switch",
18742
+ fieldPath: "on"
18743
+ },
18503
18744
  commandCaps: [
18504
18745
  "switch",
18505
18746
  "brightness",
18506
18747
  "color"
18507
18748
  ]
18749
+ }),
18750
+ actuator({
18751
+ id: "fan",
18752
+ 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.",
18753
+ deviceType: DeviceType.Fan,
18754
+ outputCap: "switch",
18755
+ outputField: "on",
18756
+ member: {
18757
+ cap: "switch",
18758
+ fieldPath: "on"
18759
+ },
18760
+ commandCaps: ["switch", "fan-control"]
18761
+ }),
18762
+ actuator({
18763
+ id: "cover",
18764
+ 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.",
18765
+ deviceType: DeviceType.Cover,
18766
+ outputCap: "group-active",
18767
+ outputField: "active",
18768
+ member: {
18769
+ cap: "cover",
18770
+ fieldPath: "state"
18771
+ },
18772
+ activeWhen: [
18773
+ "open",
18774
+ "opening",
18775
+ "stopped"
18776
+ ],
18777
+ commandCaps: ["cover"]
18778
+ }),
18779
+ actuator({
18780
+ id: "valve",
18781
+ 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.",
18782
+ deviceType: DeviceType.Valve,
18783
+ outputCap: "group-active",
18784
+ outputField: "active",
18785
+ member: {
18786
+ cap: "valve",
18787
+ fieldPath: "state"
18788
+ },
18789
+ activeWhen: [
18790
+ "open",
18791
+ "opening",
18792
+ "stopped"
18793
+ ],
18794
+ commandCaps: ["valve"]
18795
+ }),
18796
+ actuator({
18797
+ id: "lock",
18798
+ 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.",
18799
+ deviceType: DeviceType.Lock,
18800
+ outputCap: "group-active",
18801
+ outputField: "active",
18802
+ member: {
18803
+ cap: "lock-control",
18804
+ fieldPath: "state"
18805
+ },
18806
+ activeWhen: ["locked"],
18807
+ commandCaps: ["lock-control"]
18808
+ }),
18809
+ actuator({
18810
+ id: "media-player",
18811
+ 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.",
18812
+ deviceType: DeviceType.MediaPlayer,
18813
+ outputCap: "group-active",
18814
+ outputField: "active",
18815
+ member: {
18816
+ cap: "media-player",
18817
+ fieldPath: "state"
18818
+ },
18819
+ activeWhen: ["playing", "buffering"],
18820
+ commandCaps: ["media-player"]
18821
+ }),
18822
+ actuator({
18823
+ id: "climate",
18824
+ 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.",
18825
+ deviceType: DeviceType.Thermostat,
18826
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
18827
+ outputCap: "group-active",
18828
+ outputField: "active",
18829
+ member: {
18830
+ cap: "climate-control",
18831
+ fieldPath: "mode"
18832
+ },
18833
+ activeWhen: [
18834
+ "heat",
18835
+ "cool",
18836
+ "auto",
18837
+ "heat_cool",
18838
+ "fan_only",
18839
+ "dry"
18840
+ ],
18841
+ commandCaps: ["climate-control"]
18508
18842
  })
18509
18843
  ];
18510
18844
  function actuator(input) {
18511
18845
  return {
18512
- ...input,
18846
+ id: input.id,
18847
+ summary: input.summary,
18848
+ deviceType: input.deviceType,
18849
+ outputCap: input.outputCap,
18850
+ outputField: input.outputField,
18851
+ commandCaps: input.commandCaps,
18513
18852
  family: "actuator",
18514
18853
  role: void 0,
18515
18854
  memberDeviceType: input.deviceType,
18516
- outputField: "on",
18855
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
18856
+ activeWhen: input.activeWhen,
18517
18857
  quantity: "none",
18518
18858
  baseUnit: void 0,
18519
18859
  aggregates: BINARY_AGGREGATES,
18520
18860
  defaultAggregate: "any",
18521
18861
  empty: "offline",
18522
- accept: [{
18523
- cap: "switch",
18524
- fieldPath: "on"
18525
- }],
18862
+ accept: [input.member],
18526
18863
  companions: [CHANGED]
18527
18864
  };
18528
18865
  }
18529
18866
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
18530
18867
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
18868
+ DeviceType.Camera;
18531
18869
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
18532
18870
  /**
18533
18871
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -43847,7 +44185,7 @@ var NetworkAccessStatusSchema = object({
43847
44185
  });
43848
44186
  /**
43849
44187
  * Optional, richer endpoint shape returned by providers that expose
43850
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
44188
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
43851
44189
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
43852
44190
  * the originating provider config (mode + sourcePort) so the
43853
44191
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -46534,7 +46872,20 @@ var NcRuleInputSchema = object({
46534
46872
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
46535
46873
  * path does not run Zod defaults.
46536
46874
  */
46537
- groupingSec: number().int().min(5).max(60).optional()
46875
+ groupingSec: number().int().min(5).max(60).optional(),
46876
+ /**
46877
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
46878
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
46879
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
46880
+ * same mark the Library writes, and records whose notification it was and
46881
+ * when. A channel with no buttons gets the same two choices as links.
46882
+ *
46883
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
46884
+ * cap path): absent and `false` both mean off, which is what every rule
46885
+ * authored before this field meant. An editor turns it off by writing
46886
+ * `false`, so it needs no `clear` key.
46887
+ */
46888
+ askVerdict: boolean().optional()
46538
46889
  });
46539
46890
  /**
46540
46891
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -57767,7 +58118,8 @@ var BATTERY_DEVICE_TYPES = [
57767
58118
  DeviceType.Switch,
57768
58119
  DeviceType.PetFeeder,
57769
58120
  DeviceType.Container,
57770
- DeviceType.Thermostat
58121
+ DeviceType.Thermostat,
58122
+ DeviceType.EventEmitter
57771
58123
  ];
57772
58124
  /** Generic Customize target for the percentage reading (D936). */
57773
58125
  var batteryCustomize = {
@@ -60627,7 +60979,17 @@ var groupCommandsCapability = {
60627
60979
  scope: "device",
60628
60980
  deviceNative: true,
60629
60981
  mode: "singleton",
60630
- deviceTypes: [DeviceType.Switch, DeviceType.Light],
60982
+ deviceTypes: [
60983
+ DeviceType.Switch,
60984
+ DeviceType.Light,
60985
+ DeviceType.Fan,
60986
+ DeviceType.Cover,
60987
+ DeviceType.Valve,
60988
+ DeviceType.Lock,
60989
+ DeviceType.MediaPlayer,
60990
+ DeviceType.Thermostat,
60991
+ DeviceType.Climate
60992
+ ],
60631
60993
  methods: {},
60632
60994
  status: {
60633
60995
  schema: GroupCommandsSchema,
@@ -60645,6 +61007,49 @@ var groupCommandsCapability = {
60645
61007
  history: "none"
60646
61008
  };
60647
61009
  /**
61010
+ * Whether a helper actuator group counts as active (D944): at least one
61011
+ * included member is, or every included member is. What "active" means is the
61012
+ * type's: a cover is open, a lock is locked, a media player is playing, a
61013
+ * climate device is not off. A group of lights, switches and fans reports
61014
+ * `switch.on` instead; this cap is for the types whose own state is not a
61015
+ * yes/no. Unknown members are left out (`group-status` counts them) and an
61016
+ * empty included set takes the device offline, never `active: false`.
61017
+ */
61018
+ var GroupActiveSchema = object({
61019
+ active: boolean().meta({ label: "Active" }),
61020
+ /** Ms epoch when the slice was last updated. */
61021
+ lastChangedAt: number()
61022
+ });
61023
+ var groupActiveCapability = {
61024
+ name: "group-active",
61025
+ scope: "device",
61026
+ deviceNative: true,
61027
+ mode: "singleton",
61028
+ deviceTypes: [
61029
+ DeviceType.Cover,
61030
+ DeviceType.Valve,
61031
+ DeviceType.Lock,
61032
+ DeviceType.MediaPlayer,
61033
+ DeviceType.Climate,
61034
+ DeviceType.Thermostat
61035
+ ],
61036
+ methods: {},
61037
+ status: {
61038
+ schema: GroupActiveSchema,
61039
+ kind: "push"
61040
+ },
61041
+ runtimeState: GroupActiveSchema,
61042
+ /**
61043
+ * Runtime-state durability: **session** — recomputed from the members on the
61044
+ * next source event; a restored value would outlive the members it named.
61045
+ *
61046
+ * See `RuntimeStateDurability`. Enforced by
61047
+ * `scripts/check-runtime-state-durability.ts`.
61048
+ */
61049
+ durability: "session",
61050
+ history: "none"
61051
+ };
61052
+ /**
60648
61053
  * How many members of a helper group answered (D944). Counts are integers,
60649
61054
  * not measurements: zero included is a real count, never a stand-in reading.
60650
61055
  * The reading itself stays on the output capability. History is none — the
@@ -60675,7 +61080,14 @@ var groupStatusCapability = {
60675
61080
  deviceTypes: [
60676
61081
  DeviceType.Sensor,
60677
61082
  DeviceType.Switch,
60678
- DeviceType.Light
61083
+ DeviceType.Light,
61084
+ DeviceType.Fan,
61085
+ DeviceType.Cover,
61086
+ DeviceType.Valve,
61087
+ DeviceType.Lock,
61088
+ DeviceType.MediaPlayer,
61089
+ DeviceType.Thermostat,
61090
+ DeviceType.Climate
60679
61091
  ],
60680
61092
  methods: {},
60681
61093
  status: {
@@ -61948,7 +62360,14 @@ var lockControlCapability = {
61948
62360
  }),
61949
62361
  unlock: method(object({
61950
62362
  deviceId: number().int().nonnegative(),
61951
- code: string().min(1).optional()
62363
+ code: string().min(1).optional(),
62364
+ /**
62365
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62366
+ * with one call is refused unless the caller says it was confirmed. A
62367
+ * single lock ignores it. The refusal lives in the callee, so no
62368
+ * automation or API caller reaches the doors by skipping a dialog.
62369
+ */
62370
+ confirm: boolean().optional()
61952
62371
  }), _void(), {
61953
62372
  kind: "mutation",
61954
62373
  auth: "protected"
@@ -61985,6 +62404,44 @@ var lockControlCapability = {
61985
62404
  volatileStateFields: ["lastChangedAt"]
61986
62405
  };
61987
62406
  /**
62407
+ * A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
62408
+ * so dashboards keep one series; every other instantaneous reading lands here.
62409
+ * `value` is the time integral in the source quantity's accumulated base
62410
+ * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
62411
+ */
62412
+ var MeterStatusSchema = object({
62413
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
62414
+ lastFetchedAt: number()
62415
+ });
62416
+ var meterCapability = {
62417
+ name: "meter",
62418
+ scope: "device",
62419
+ deviceNative: true,
62420
+ mode: "singleton",
62421
+ deviceTypes: [DeviceType.Sensor],
62422
+ methods: {},
62423
+ status: {
62424
+ schema: MeterStatusSchema,
62425
+ kind: "push"
62426
+ },
62427
+ runtimeState: MeterStatusSchema,
62428
+ /**
62429
+ * Runtime-state durability: **restored** — the total is a cache of the
62430
+ * history integral. A restart shows it only until the history pass
62431
+ * recomputes the open period; it is not an in-memory buffer.
62432
+ *
62433
+ * See `RuntimeStateDurability`. Enforced by
62434
+ * `scripts/check-runtime-state-durability.ts`.
62435
+ */
62436
+ durability: "restored",
62437
+ history: { fields: { value: {
62438
+ kind: "counter",
62439
+ deadband: .001,
62440
+ minIntervalMs: 6e4
62441
+ } } },
62442
+ volatileStateFields: ["lastFetchedAt"]
62443
+ };
62444
+ /**
61988
62445
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
61989
62446
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
61990
62447
  * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
@@ -64542,9 +64999,9 @@ var PowerMeterStatusSchema = object({
64542
64999
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64543
65000
  kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64544
65001
  /** Voltage in volts. */
64545
- volts: number().optional().meta(readingMeta("voltage")),
65002
+ volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64546
65003
  /** Current in amperes. */
64547
- amps: number().optional().meta(readingMeta("current")),
65004
+ amps: number().optional().meta(readingMeta("current", { label: "Current" })),
64548
65005
  /** Apparent power in volt-amperes. */
64549
65006
  va: number().optional().meta(readingMeta("apparent-power")),
64550
65007
  /** Power factor (0..1) if reported. */
@@ -64552,12 +65009,74 @@ var PowerMeterStatusSchema = object({
64552
65009
  /** Ms epoch when the slice was last updated. */
64553
65010
  lastFetchedAt: number()
64554
65011
  });
65012
+ /**
65013
+ * Loads that draw metered mains power (D951). The reading is a feature of the
65014
+ * device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
65015
+ * vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
65016
+ * meter or a standalone energy monitor IS the device. Cover, lock, valve,
65017
+ * camera and the like are left out: their draw is not what anyone meters.
65018
+ */
65019
+ var POWER_METER_DEVICE_TYPES = [
65020
+ DeviceType.Sensor,
65021
+ DeviceType.Switch,
65022
+ DeviceType.Light,
65023
+ DeviceType.Fan,
65024
+ DeviceType.Climate,
65025
+ DeviceType.Thermostat,
65026
+ DeviceType.Humidifier,
65027
+ DeviceType.WaterHeater,
65028
+ DeviceType.MediaPlayer,
65029
+ DeviceType.Washer,
65030
+ DeviceType.Dryer,
65031
+ DeviceType.Dishwasher,
65032
+ DeviceType.Vacuum,
65033
+ DeviceType.LawnMower,
65034
+ DeviceType.PetFeeder
65035
+ ];
65036
+ /** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
65037
+ var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
65038
+ /** One scalar target per reading; each is sourced from a device of its own (D951). */
65039
+ function powerTarget(id, unit) {
65040
+ return {
65041
+ id,
65042
+ cardinality: "scalar",
65043
+ vocabulary: {
65044
+ cap: "power-meter",
65045
+ stateField: id
65046
+ },
65047
+ deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
65048
+ apply: "add",
65049
+ path: id,
65050
+ sources: [{
65051
+ kind: "numeric",
65052
+ requiredUnit: unit,
65053
+ transforms: [{ kind: "identity" }]
65054
+ }],
65055
+ companions: [{
65056
+ kind: "clock",
65057
+ path: "lastFetchedAt",
65058
+ when: "always"
65059
+ }],
65060
+ sourceReport: { status: "assignment-status" },
65061
+ advancedExpression: true
65062
+ };
65063
+ }
65064
+ /** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
65065
+ var powerMeterCustomize = {
65066
+ version: 1,
65067
+ targets: [
65068
+ powerTarget("watts", "W"),
65069
+ powerTarget("kwhTotal", "kWh"),
65070
+ powerTarget("volts", "V"),
65071
+ powerTarget("amps", "A")
65072
+ ]
65073
+ };
64555
65074
  var powerMeterCapability = {
64556
65075
  name: "power-meter",
64557
65076
  scope: "device",
64558
65077
  deviceNative: true,
64559
65078
  mode: "singleton",
64560
- deviceTypes: [DeviceType.Sensor],
65079
+ deviceTypes: POWER_METER_DEVICE_TYPES,
64561
65080
  methods: {},
64562
65081
  status: {
64563
65082
  schema: PowerMeterStatusSchema,
@@ -64614,7 +65133,8 @@ var powerMeterCapability = {
64614
65133
  } },
64615
65134
  /** Clock fields: written, but excluded from the compare that decides
64616
65135
  * whether persisting is worth a SQLite commit. */
64617
- volatileStateFields: ["lastFetchedAt"]
65136
+ volatileStateFields: ["lastFetchedAt"],
65137
+ customize: powerMeterCustomize
64618
65138
  };
64619
65139
  /**
64620
65140
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -65407,6 +65927,8 @@ var runningCustomize = {
65407
65927
  stateField: "running"
65408
65928
  },
65409
65929
  deviceTypes: [
65930
+ DeviceType.Washer,
65931
+ DeviceType.Dryer,
65410
65932
  DeviceType.Dishwasher,
65411
65933
  DeviceType.Vacuum,
65412
65934
  DeviceType.LawnMower,
@@ -68200,7 +68722,8 @@ var switchCapability = {
68200
68722
  deviceTypes: [
68201
68723
  DeviceType.Switch,
68202
68724
  DeviceType.Siren,
68203
- DeviceType.Light
68725
+ DeviceType.Light,
68726
+ DeviceType.Fan
68204
68727
  ],
68205
68728
  methods: { setState: method(object({
68206
68729
  deviceId: number(),
@@ -70546,8 +71069,9 @@ function withClipEventLabels(provider, deps) {
70546
71069
  listClips
70547
71070
  };
70548
71071
  }
71072
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
70549
71073
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
70550
- 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;
71074
+ 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;
70551
71075
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
70552
71076
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
70553
71077
  new Set(CAP_NAMES);
@@ -70876,6 +71400,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70876
71400
  featureProbe: featureProbeCapability,
70877
71401
  flood: floodCapability,
70878
71402
  gas: gasCapability,
71403
+ groupActive: groupActiveCapability,
70879
71404
  groupCommands: groupCommandsCapability,
70880
71405
  groupStatus: groupStatusCapability,
70881
71406
  heatingValve: heatingValveCapability,
@@ -70887,6 +71412,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
70887
71412
  lawnMowerControl: lawnMowerControlCapability,
70888
71413
  lockControl: lockControlCapability,
70889
71414
  mediaPlayer: mediaPlayerCapability,
71415
+ meter: meterCapability,
70890
71416
  motion: motionCapability,
70891
71417
  motionTrigger: motionTriggerCapability,
70892
71418
  motionZones: motionZonesCapability,
@@ -72150,7 +72676,7 @@ var BaseDeviceProvider = class extends BaseAddon {
72150
72676
  function widenDeviceConstructor(ctor) {
72151
72677
  return ctor;
72152
72678
  }
72153
- 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;
72679
+ 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;
72154
72680
  new Set(Object.values(DeviceType));
72155
72681
  Object.freeze({
72156
72682
  "accessories.getStatus": {
@@ -75373,6 +75899,13 @@ Object.freeze({
75373
75899
  access: "view",
75374
75900
  role: "viewer"
75375
75901
  },
75902
+ "groupActive.getStatus": {
75903
+ capName: "group-active",
75904
+ capScope: "device",
75905
+ addonId: null,
75906
+ access: "view",
75907
+ role: "viewer"
75908
+ },
75376
75909
  "groupCommands.getStatus": {
75377
75910
  capName: "group-commands",
75378
75911
  capScope: "device",
@@ -76339,6 +76872,13 @@ Object.freeze({
76339
76872
  access: "create",
76340
76873
  role: "admin"
76341
76874
  },
76875
+ "meter.getStatus": {
76876
+ capName: "meter",
76877
+ capScope: "device",
76878
+ addonId: null,
76879
+ access: "view",
76880
+ role: "viewer"
76881
+ },
76342
76882
  "metricsProvider.collectSnapshot": {
76343
76883
  capName: "metrics-provider",
76344
76884
  capScope: "system",
@@ -84437,7 +84977,39 @@ var APPLIANCE_CAP_VOCABULARY = {
84437
84977
  phrase: t("When {device} finishes", "Quando {device} finisce"),
84438
84978
  icon: "square"
84439
84979
  }
84440
- }
84980
+ },
84981
+ overrides: { byType: {
84982
+ [DeviceType.Washer]: {
84983
+ state: { running: {
84984
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
84985
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
84986
+ } },
84987
+ events: {
84988
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
84989
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
84990
+ }
84991
+ },
84992
+ [DeviceType.Dryer]: {
84993
+ state: { running: {
84994
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
84995
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
84996
+ } },
84997
+ events: {
84998
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
84999
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
85000
+ }
85001
+ },
85002
+ [DeviceType.Dishwasher]: {
85003
+ state: { running: {
85004
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
85005
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
85006
+ } },
85007
+ events: {
85008
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
85009
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
85010
+ }
85011
+ }
85012
+ } }
84441
85013
  },
84442
85014
  update: {
84443
85015
  scope: "device",
@@ -85209,6 +85781,10 @@ var SENSOR_CAP_VOCABULARY = {
85209
85781
  icon: "flame"
85210
85782
  }]
85211
85783
  },
85784
+ "group-active": {
85785
+ scope: "device",
85786
+ noun: t("Group state", "Stato del gruppo")
85787
+ },
85212
85788
  "group-commands": {
85213
85789
  scope: "device",
85214
85790
  noun: t("Group command", "Comando di gruppo")
@@ -85265,6 +85841,17 @@ var SENSOR_CAP_VOCABULARY = {
85265
85841
  }],
85266
85842
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
85267
85843
  },
85844
+ meter: {
85845
+ scope: "device",
85846
+ noun: t("Meter", "Contatore"),
85847
+ state: [{
85848
+ kind: "numeric",
85849
+ field: "value",
85850
+ quantity: t("the total", "il totale"),
85851
+ label: t("Total", "Totale"),
85852
+ icon: "gauge"
85853
+ }]
85854
+ },
85268
85855
  "numeric-sensor": {
85269
85856
  scope: "device",
85270
85857
  noun: t("Sensor", "Sensore"),
@@ -85737,8 +86324,47 @@ function projectAction(cap, action) {
85737
86324
  };
85738
86325
  }
85739
86326
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
85740
- 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;
86327
+ 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;
85741
86328
  DeviceFeature.BatteryOperated;
86329
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
86330
+ 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.";
86331
+ 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.";
86332
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
86333
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
86334
+ 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.";
86335
+ function withheld(reason) {
86336
+ return {
86337
+ [DeviceType.Washer]: {
86338
+ exported: false,
86339
+ reason
86340
+ },
86341
+ [DeviceType.Dryer]: {
86342
+ exported: false,
86343
+ reason
86344
+ },
86345
+ [DeviceType.Dishwasher]: {
86346
+ exported: false,
86347
+ reason
86348
+ }
86349
+ };
86350
+ }
86351
+ function offered(received) {
86352
+ return {
86353
+ [DeviceType.Washer]: {
86354
+ exported: true,
86355
+ as: received
86356
+ },
86357
+ [DeviceType.Dryer]: {
86358
+ exported: true,
86359
+ as: received
86360
+ },
86361
+ [DeviceType.Dishwasher]: {
86362
+ exported: true,
86363
+ as: received
86364
+ }
86365
+ };
86366
+ }
86367
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
85742
86368
  /** Reject after `ms`; always clears its own timer. */
85743
86369
  async function withTimeout$1(promise, ms, label) {
85744
86370
  let timer;
@@ -86911,6 +87537,11 @@ Object.freeze({
86911
87537
  form: "single",
86912
87538
  optional: false
86913
87539
  }],
87540
+ "groupActive.getStatus": [{
87541
+ name: "deviceId",
87542
+ form: "single",
87543
+ optional: false
87544
+ }],
86914
87545
  "groupCommands.getStatus": [{
86915
87546
  name: "deviceId",
86916
87547
  form: "single",
@@ -87121,6 +87752,11 @@ Object.freeze({
87121
87752
  form: "single",
87122
87753
  optional: false
87123
87754
  }],
87755
+ "meter.getStatus": [{
87756
+ name: "deviceId",
87757
+ form: "single",
87758
+ optional: false
87759
+ }],
87124
87760
  "motion.getStatus": [{
87125
87761
  name: "deviceId",
87126
87762
  form: "single",
@@ -88728,7 +89364,6 @@ Object.freeze({
88728
89364
  "network-access": "ingress",
88729
89365
  "smtp-provider": "email"
88730
89366
  });
88731
- DeviceType.Camera;
88732
89367
  /**
88733
89368
  * A saved Customize assignment (D936). The schema is the type: callers use
88734
89369
  * `z.infer`, they do not restate the shape.