@camstack/addon-notifiers 1.2.622 → 1.2.623

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
@@ -5605,7 +5605,7 @@ var ZodIssueCode = {
5605
5605
  var ZodFirstPartyTypeKind;
5606
5606
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5607
5607
  //#endregion
5608
- //#region ../types/dist/sleep-bykKrTgu.mjs
5608
+ //#region ../types/dist/sleep--jXS9hWo.mjs
5609
5609
  /**
5610
5610
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5611
5611
  * window to float samples (D455).
@@ -8962,6 +8962,7 @@ var CAP_NAMES = Object.freeze([
8962
8962
  "filesystem-browse",
8963
8963
  "flood",
8964
8964
  "gas",
8965
+ "group-active",
8965
8966
  "group-commands",
8966
8967
  "group-status",
8967
8968
  "heating-valve",
@@ -8984,6 +8985,7 @@ var CAP_NAMES = Object.freeze([
8984
8985
  "login-method",
8985
8986
  "media-player",
8986
8987
  "mesh-network",
8988
+ "meter",
8987
8989
  "metrics-provider",
8988
8990
  "model-convert",
8989
8991
  "model-distributor",
@@ -9782,6 +9784,10 @@ Object.freeze({
9782
9784
  "path": "detected",
9783
9785
  "valueType": "boolean"
9784
9786
  }],
9787
+ "group-active": [{
9788
+ "path": "active",
9789
+ "valueType": "boolean"
9790
+ }],
9785
9791
  "group-status": [
9786
9792
  {
9787
9793
  "path": "included",
@@ -10041,6 +10047,10 @@ Object.freeze({
10041
10047
  ]
10042
10048
  }
10043
10049
  ],
10050
+ "meter": [{
10051
+ "path": "value",
10052
+ "valueType": "number"
10053
+ }],
10044
10054
  "motion": [
10045
10055
  {
10046
10056
  "path": "detected",
@@ -10798,7 +10808,7 @@ function userError(code, params, englishFallback) {
10798
10808
  return new CamstackError(code, params, englishFallback);
10799
10809
  }
10800
10810
  //#endregion
10801
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
10811
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
10802
10812
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10803
10813
  DeviceType["Camera"] = "camera";
10804
10814
  DeviceType["Hub"] = "hub";
@@ -10892,8 +10902,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10892
10902
  DeviceType["PetFeeder"] = "pet-feeder";
10893
10903
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10894
10904
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10895
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
10905
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
10906
+ * A plug-built dishwasher carries running-state and power instead. */
10896
10907
  DeviceType["Dishwasher"] = "dishwasher";
10908
+ /** Washing machine. A plug-built one is running while its power stays
10909
+ * above a threshold. No program cap — that surface is the dishwasher's. */
10910
+ DeviceType["Washer"] = "washer";
10911
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10912
+ DeviceType["Dryer"] = "dryer";
10897
10913
  return DeviceType;
10898
10914
  }({});
10899
10915
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11141,7 +11157,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
11141
11157
  return DeviceRole;
11142
11158
  }({});
11143
11159
  //#endregion
11144
- //#region ../types/dist/composition-OhpuespA.mjs
11160
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
11145
11161
  /**
11146
11162
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11147
11163
  *
@@ -11456,6 +11472,13 @@ var table = {
11456
11472
  apply: () => {
11457
11473
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
11458
11474
  }
11475
+ },
11476
+ schmitt: {
11477
+ minArgs: 3,
11478
+ maxArgs: 5,
11479
+ apply: () => {
11480
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
11481
+ }
11459
11482
  }
11460
11483
  };
11461
11484
  Object.freeze(Object.assign(Object.create(null), table));
@@ -11897,6 +11920,99 @@ function validateExpressionSource(src) {
11897
11920
  return null;
11898
11921
  }
11899
11922
  /**
11923
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
11924
+ *
11925
+ * Standby of the control board is under 5 W; a motor or a heater is far
11926
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
11927
+ * a cycle, so running starts only after 30 s above the line.
11928
+ *
11929
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
11930
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
11931
+ * report "finished" in the middle of the program, so the stop dwell is
11932
+ * 10 minutes on every type. The number is stored per type so one machine
11933
+ * can move later without moving the others.
11934
+ */
11935
+ var APPLIANCE_TEMPLATE_ID = "appliance";
11936
+ var APPLIANCE_KINDS = [
11937
+ "washer",
11938
+ "dryer",
11939
+ "dishwasher"
11940
+ ];
11941
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
11942
+ /**
11943
+ * Statistics and integrals over a history read (D944).
11944
+ *
11945
+ * Pure: the same points always give the same answer, so a restart recomputes
11946
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
11947
+ * never the mean of the samples. A hole with no held value is not integrated
11948
+ * as zero. A truncated read is not published.
11949
+ */
11950
+ var STATISTIC_STATS = [
11951
+ "min",
11952
+ "max",
11953
+ "mean",
11954
+ "sum",
11955
+ "count",
11956
+ "change"
11957
+ ];
11958
+ /**
11959
+ * The range a statistics or integral helper asks history for (D944).
11960
+ *
11961
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
11962
+ * or 25 hours across a DST change. A tier is eligible only when its retention
11963
+ * is strictly longer than the longest that range can be. Forever qualifies.
11964
+ * Equal to a tier's retention is a refusal, not a short answer.
11965
+ */
11966
+ var MINUTE_MS = 6e4;
11967
+ var HOUR_MS = 60 * MINUTE_MS;
11968
+ var DAY_MS = 24 * HOUR_MS;
11969
+ var ROLLING_SPANS = [
11970
+ "1h",
11971
+ "24h",
11972
+ "2d",
11973
+ "7d",
11974
+ "30d",
11975
+ "custom"
11976
+ ];
11977
+ var CALENDAR_PERIODS = [
11978
+ "today",
11979
+ "yesterday",
11980
+ "this-week",
11981
+ "this-month"
11982
+ ];
11983
+ var RESET_PERIODS = [
11984
+ "never",
11985
+ "daily",
11986
+ "weekly",
11987
+ "monthly"
11988
+ ];
11989
+ 5 * MINUTE_MS;
11990
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
11991
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
11992
+ var rollingWindowObject = object({
11993
+ kind: literal("rolling"),
11994
+ span: _enum(ROLLING_SPANS),
11995
+ customMinutes: number().int().min(5).optional()
11996
+ }).check((payload) => {
11997
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
11998
+ code: "custom",
11999
+ message: "a custom range needs a duration in minutes",
12000
+ path: ["customMinutes"],
12001
+ input: payload.value
12002
+ });
12003
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
12004
+ code: "custom",
12005
+ message: "only a custom range carries a duration",
12006
+ path: ["customMinutes"],
12007
+ input: payload.value
12008
+ });
12009
+ });
12010
+ var RollingWindowSchema = rollingWindowObject;
12011
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
12012
+ kind: literal("calendar"),
12013
+ period: _enum(CALENDAR_PERIODS)
12014
+ })]);
12015
+ /**
11900
12016
  * A composition source identity: `(addonId, stableId)`, never a numeric device
11901
12017
  * id. Split out so helper schemas can name it without a cycle through
11902
12018
  * `composition.ts` (D659).
@@ -11922,7 +12038,13 @@ var GROUP_TEMPLATE_IDS = [
11922
12038
  "occupancy",
11923
12039
  "binary",
11924
12040
  "switch",
11925
- "light"
12041
+ "light",
12042
+ "fan",
12043
+ "cover",
12044
+ "valve",
12045
+ "lock",
12046
+ "media-player",
12047
+ "climate"
11926
12048
  ];
11927
12049
  var NUMERIC_AGGREGATES = [
11928
12050
  "mean",
@@ -11952,14 +12074,73 @@ var HelperMemberSchema = object({
11952
12074
  cap: string().min(1),
11953
12075
  fieldPath: string().min(1)
11954
12076
  });
11955
- var HelperSpecSchema = discriminatedUnion("kind", [object({
11956
- kind: literal("group"),
11957
- templateId: GroupTemplateIdSchema,
11958
- aggregate: AggregateReducerSchema,
11959
- members: array(HelperMemberSchema).min(1).max(64),
11960
- /** Actuator groups only. Readings leave this unset. */
11961
- active: ActiveCriterionSchema.optional()
11962
- })]);
12077
+ var HelperSpecSchema = discriminatedUnion("kind", [
12078
+ object({
12079
+ kind: literal("group"),
12080
+ templateId: GroupTemplateIdSchema,
12081
+ aggregate: AggregateReducerSchema,
12082
+ members: array(HelperMemberSchema).min(1).max(64),
12083
+ /** Actuator groups only. Readings leave this unset. */
12084
+ active: ActiveCriterionSchema.optional()
12085
+ }),
12086
+ object({
12087
+ kind: literal("appliance"),
12088
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
12089
+ appliance: _enum(APPLIANCE_KINDS),
12090
+ thresholdWatts: number().positive(),
12091
+ startDwellMs: number().int().nonnegative(),
12092
+ stopDwellMs: number().int().nonnegative(),
12093
+ member: HelperMemberSchema
12094
+ }),
12095
+ object({
12096
+ kind: literal("statistic"),
12097
+ stat: _enum(STATISTIC_STATS),
12098
+ window: StatisticWindowSchema,
12099
+ source: HelperMemberSchema
12100
+ }),
12101
+ object({
12102
+ kind: literal("integral"),
12103
+ reset: _enum(RESET_PERIODS),
12104
+ output: _enum(["energy", "meter"]),
12105
+ source: HelperMemberSchema
12106
+ }),
12107
+ object({
12108
+ kind: literal("threshold"),
12109
+ source: HelperMemberSchema,
12110
+ high: number(),
12111
+ low: number(),
12112
+ onForMs: number().int().nonnegative(),
12113
+ offForMs: number().int().nonnegative()
12114
+ }),
12115
+ object({
12116
+ kind: literal("arithmetic"),
12117
+ op: _enum([
12118
+ "sum",
12119
+ "difference",
12120
+ "ratio"
12121
+ ]),
12122
+ inputs: array(HelperMemberSchema).min(2).max(64)
12123
+ }),
12124
+ object({
12125
+ kind: literal("expression"),
12126
+ expr: string().min(1),
12127
+ inputs: array(object({
12128
+ name: string().min(1),
12129
+ member: HelperMemberSchema
12130
+ })).min(1).max(64),
12131
+ output: _enum(["number", "boolean"])
12132
+ }),
12133
+ object({
12134
+ kind: literal("derivative"),
12135
+ window: RollingWindowSchema,
12136
+ per: _enum([
12137
+ "second",
12138
+ "minute",
12139
+ "hour"
12140
+ ]),
12141
+ source: HelperMemberSchema
12142
+ })
12143
+ ]);
11963
12144
  /** A composed device's stableId is this prefix plus the block id. */
11964
12145
  var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11965
12146
  /** Longest snippet a `code` source may carry (slice 4). */
@@ -12020,6 +12201,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12020
12201
  "all"
12021
12202
  ]),
12022
12203
  reads: array(CompositionFieldReadSchema).min(1).max(64),
12204
+ /**
12205
+ * Set when the reads are an enum state, not a yes/no: the listed states
12206
+ * count as active (a cover is `open`). A state outside the list is inactive;
12207
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
12208
+ */
12209
+ activeWhen: array(string().min(1)).min(1).optional(),
12023
12210
  /** `value` is the reading; the others are the member counts. */
12024
12211
  publish: _enum([
12025
12212
  "value",
@@ -12033,6 +12220,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12033
12220
  "omit",
12034
12221
  "offline"
12035
12222
  ])
12223
+ }),
12224
+ object({
12225
+ kind: literal("series"),
12226
+ read: CompositionFieldReadSchema,
12227
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12228
+ publish: _enum([
12229
+ "value",
12230
+ "included",
12231
+ "excluded",
12232
+ "total"
12233
+ ]),
12234
+ /**
12235
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12236
+ * device unseeded until history answers, so a restart cannot publish 0.
12237
+ */
12238
+ empty: _enum([
12239
+ "null",
12240
+ "omit",
12241
+ "offline"
12242
+ ])
12036
12243
  })
12037
12244
  ]);
12038
12245
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -17328,7 +17535,22 @@ var BrokerProviderInfoSchema = object({
17328
17535
  */
17329
17536
  interactiveConnectPath: string().min(1).optional(),
17330
17537
  /** Relative hub path to disconnect the linked account (same popup pattern). */
17331
- interactiveDisconnectPath: string().min(1).optional()
17538
+ interactiveDisconnectPath: string().min(1).optional(),
17539
+ /**
17540
+ * The kind signs in through a server-held interactive session instead of
17541
+ * (or before) typed credentials. `basePath` is a hub-relative route set
17542
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
17543
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
17544
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
17545
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
17546
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
17547
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
17548
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
17549
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
17550
+ * offers it as the default path; the fields the session fills are
17551
+ * marked `filledBySignIn` in the kind's settings schema.
17552
+ */
17553
+ signIn: object({ basePath: string().min(1) }).optional()
17332
17554
  }))
17333
17555
  });
17334
17556
  var ListInputSchema = object({
@@ -18327,7 +18549,8 @@ var CompositionProblemSchema = object({
18327
18549
  "native-capability-replace",
18328
18550
  "mixed-quantity",
18329
18551
  "helper-mismatch",
18330
- "member-constraint"
18552
+ "member-constraint",
18553
+ "retention"
18331
18554
  ]),
18332
18555
  path: string(),
18333
18556
  message: string()
@@ -18561,13 +18784,17 @@ Object.freeze({
18561
18784
  latchedAt: {
18562
18785
  kind: "fixed",
18563
18786
  result: ["number", "null"]
18787
+ },
18788
+ schmitt: {
18789
+ kind: "fixed",
18790
+ result: ["boolean", "null"]
18564
18791
  }
18565
18792
  });
18566
18793
  /**
18567
18794
  * One catalog row per base reading a group helper can build (D944 §3).
18568
18795
  * Data, not a form default written into the block.
18569
18796
  */
18570
- var NOW = {
18797
+ var NOW$2 = {
18571
18798
  fieldPath: "lastFetchedAt",
18572
18799
  expr: "now"
18573
18800
  };
@@ -18581,6 +18808,8 @@ function numeric(input) {
18581
18808
  family: "numeric",
18582
18809
  deviceType: DeviceType.Sensor,
18583
18810
  memberDeviceType: DeviceType.Sensor,
18811
+ memberDeviceTypes: [DeviceType.Sensor],
18812
+ activeWhen: void 0,
18584
18813
  commandCaps: [],
18585
18814
  baseUnit: baseUnitOf(input.quantity),
18586
18815
  aggregates: NUMERIC_AGGREGATES,
@@ -18596,6 +18825,8 @@ function binary(input) {
18596
18825
  family: "binary",
18597
18826
  deviceType: DeviceType.Sensor,
18598
18827
  memberDeviceType: DeviceType.Sensor,
18828
+ memberDeviceTypes: [DeviceType.Sensor],
18829
+ activeWhen: void 0,
18599
18830
  commandCaps: [],
18600
18831
  quantity: "none",
18601
18832
  baseUnit: void 0,
@@ -18613,7 +18844,7 @@ var GROUP_TEMPLATES = [
18613
18844
  quantity: "temperature",
18614
18845
  defaultAggregate: "mean",
18615
18846
  empty: "null",
18616
- companions: [NOW]
18847
+ companions: [NOW$2]
18617
18848
  }),
18618
18849
  numeric({
18619
18850
  id: "humidity",
@@ -18624,7 +18855,7 @@ var GROUP_TEMPLATES = [
18624
18855
  quantity: "humidity",
18625
18856
  defaultAggregate: "mean",
18626
18857
  empty: "null",
18627
- companions: [NOW]
18858
+ companions: [NOW$2]
18628
18859
  }),
18629
18860
  numeric({
18630
18861
  id: "pressure",
@@ -18635,7 +18866,7 @@ var GROUP_TEMPLATES = [
18635
18866
  quantity: "pressure",
18636
18867
  defaultAggregate: "mean",
18637
18868
  empty: "null",
18638
- companions: [NOW]
18869
+ companions: [NOW$2]
18639
18870
  }),
18640
18871
  numeric({
18641
18872
  id: "illuminance",
@@ -18646,7 +18877,7 @@ var GROUP_TEMPLATES = [
18646
18877
  quantity: "illuminance",
18647
18878
  defaultAggregate: "mean",
18648
18879
  empty: "null",
18649
- companions: [NOW]
18880
+ companions: [NOW$2]
18650
18881
  }),
18651
18882
  numeric({
18652
18883
  id: "power",
@@ -18657,7 +18888,7 @@ var GROUP_TEMPLATES = [
18657
18888
  quantity: "power",
18658
18889
  defaultAggregate: "sum",
18659
18890
  empty: "omit",
18660
- companions: [NOW]
18891
+ companions: [NOW$2]
18661
18892
  }),
18662
18893
  numeric({
18663
18894
  id: "energy",
@@ -18668,7 +18899,7 @@ var GROUP_TEMPLATES = [
18668
18899
  quantity: "energy",
18669
18900
  defaultAggregate: "sum",
18670
18901
  empty: "omit",
18671
- companions: [NOW]
18902
+ companions: [NOW$2]
18672
18903
  }),
18673
18904
  numeric({
18674
18905
  id: "battery",
@@ -18703,7 +18934,7 @@ var GROUP_TEMPLATES = [
18703
18934
  quantity: "device",
18704
18935
  defaultAggregate: "mean",
18705
18936
  empty: "null",
18706
- companions: [NOW]
18937
+ companions: [NOW$2]
18707
18938
  }),
18708
18939
  binary({
18709
18940
  id: "motion",
@@ -18797,6 +19028,11 @@ var GROUP_TEMPLATES = [
18797
19028
  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.",
18798
19029
  deviceType: DeviceType.Switch,
18799
19030
  outputCap: "switch",
19031
+ outputField: "on",
19032
+ member: {
19033
+ cap: "switch",
19034
+ fieldPath: "on"
19035
+ },
18800
19036
  commandCaps: ["switch"]
18801
19037
  }),
18802
19038
  actuator({
@@ -18804,34 +19040,136 @@ var GROUP_TEMPLATES = [
18804
19040
  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.",
18805
19041
  deviceType: DeviceType.Light,
18806
19042
  outputCap: "switch",
19043
+ outputField: "on",
19044
+ member: {
19045
+ cap: "switch",
19046
+ fieldPath: "on"
19047
+ },
18807
19048
  commandCaps: [
18808
19049
  "switch",
18809
19050
  "brightness",
18810
19051
  "color"
18811
19052
  ]
19053
+ }),
19054
+ actuator({
19055
+ id: "fan",
19056
+ 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.",
19057
+ deviceType: DeviceType.Fan,
19058
+ outputCap: "switch",
19059
+ outputField: "on",
19060
+ member: {
19061
+ cap: "switch",
19062
+ fieldPath: "on"
19063
+ },
19064
+ commandCaps: ["switch", "fan-control"]
19065
+ }),
19066
+ actuator({
19067
+ id: "cover",
19068
+ 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.",
19069
+ deviceType: DeviceType.Cover,
19070
+ outputCap: "group-active",
19071
+ outputField: "active",
19072
+ member: {
19073
+ cap: "cover",
19074
+ fieldPath: "state"
19075
+ },
19076
+ activeWhen: [
19077
+ "open",
19078
+ "opening",
19079
+ "stopped"
19080
+ ],
19081
+ commandCaps: ["cover"]
19082
+ }),
19083
+ actuator({
19084
+ id: "valve",
19085
+ 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.",
19086
+ deviceType: DeviceType.Valve,
19087
+ outputCap: "group-active",
19088
+ outputField: "active",
19089
+ member: {
19090
+ cap: "valve",
19091
+ fieldPath: "state"
19092
+ },
19093
+ activeWhen: [
19094
+ "open",
19095
+ "opening",
19096
+ "stopped"
19097
+ ],
19098
+ commandCaps: ["valve"]
19099
+ }),
19100
+ actuator({
19101
+ id: "lock",
19102
+ 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.",
19103
+ deviceType: DeviceType.Lock,
19104
+ outputCap: "group-active",
19105
+ outputField: "active",
19106
+ member: {
19107
+ cap: "lock-control",
19108
+ fieldPath: "state"
19109
+ },
19110
+ activeWhen: ["locked"],
19111
+ commandCaps: ["lock-control"]
19112
+ }),
19113
+ actuator({
19114
+ id: "media-player",
19115
+ 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.",
19116
+ deviceType: DeviceType.MediaPlayer,
19117
+ outputCap: "group-active",
19118
+ outputField: "active",
19119
+ member: {
19120
+ cap: "media-player",
19121
+ fieldPath: "state"
19122
+ },
19123
+ activeWhen: ["playing", "buffering"],
19124
+ commandCaps: ["media-player"]
19125
+ }),
19126
+ actuator({
19127
+ id: "climate",
19128
+ 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.",
19129
+ deviceType: DeviceType.Thermostat,
19130
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
19131
+ outputCap: "group-active",
19132
+ outputField: "active",
19133
+ member: {
19134
+ cap: "climate-control",
19135
+ fieldPath: "mode"
19136
+ },
19137
+ activeWhen: [
19138
+ "heat",
19139
+ "cool",
19140
+ "auto",
19141
+ "heat_cool",
19142
+ "fan_only",
19143
+ "dry"
19144
+ ],
19145
+ commandCaps: ["climate-control"]
18812
19146
  })
18813
19147
  ];
18814
19148
  function actuator(input) {
18815
19149
  return {
18816
- ...input,
19150
+ id: input.id,
19151
+ summary: input.summary,
19152
+ deviceType: input.deviceType,
19153
+ outputCap: input.outputCap,
19154
+ outputField: input.outputField,
19155
+ commandCaps: input.commandCaps,
18817
19156
  family: "actuator",
18818
19157
  role: void 0,
18819
19158
  memberDeviceType: input.deviceType,
18820
- outputField: "on",
19159
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
19160
+ activeWhen: input.activeWhen,
18821
19161
  quantity: "none",
18822
19162
  baseUnit: void 0,
18823
19163
  aggregates: BINARY_AGGREGATES,
18824
19164
  defaultAggregate: "any",
18825
19165
  empty: "offline",
18826
- accept: [{
18827
- cap: "switch",
18828
- fieldPath: "on"
18829
- }],
19166
+ accept: [input.member],
18830
19167
  companions: [CHANGED]
18831
19168
  };
18832
19169
  }
18833
19170
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
18834
19171
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
19172
+ DeviceType.Camera;
18835
19173
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
18836
19174
  /**
18837
19175
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -43846,7 +44184,7 @@ var NetworkAccessStatusSchema = object({
43846
44184
  });
43847
44185
  /**
43848
44186
  * Optional, richer endpoint shape returned by providers that expose
43849
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
44187
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
43850
44188
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
43851
44189
  * the originating provider config (mode + sourcePort) so the
43852
44190
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -46511,7 +46849,20 @@ var NcRuleInputSchema = object({
46511
46849
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
46512
46850
  * path does not run Zod defaults.
46513
46851
  */
46514
- groupingSec: number().int().min(5).max(60).optional()
46852
+ groupingSec: number().int().min(5).max(60).optional(),
46853
+ /**
46854
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
46855
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
46856
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
46857
+ * same mark the Library writes, and records whose notification it was and
46858
+ * when. A channel with no buttons gets the same two choices as links.
46859
+ *
46860
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
46861
+ * cap path): absent and `false` both mean off, which is what every rule
46862
+ * authored before this field meant. An editor turns it off by writing
46863
+ * `false`, so it needs no `clear` key.
46864
+ */
46865
+ askVerdict: boolean().optional()
46515
46866
  });
46516
46867
  /**
46517
46868
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -56968,7 +57319,7 @@ var BatteryStatusSchema = object({
56968
57319
  */
56969
57320
  binary: boolean().optional().meta({ label: "Binary indicator" })
56970
57321
  });
56971
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat;
57322
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter;
56972
57323
  method(object({
56973
57324
  deviceId: number(),
56974
57325
  /** Bound on the wait. Sensible range 3000–10000ms. */
@@ -58712,7 +59063,13 @@ results: array(object({
58712
59063
  /** Ms epoch of the attempt. */
58713
59064
  at: number()
58714
59065
  })) });
58715
- DeviceType.Switch, DeviceType.Light;
59066
+ DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
59067
+ object({
59068
+ active: boolean().meta({ label: "Active" }),
59069
+ /** Ms epoch when the slice was last updated. */
59070
+ lastChangedAt: number()
59071
+ });
59072
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
58716
59073
  object({
58717
59074
  /** Members whose current value entered the aggregate. */
58718
59075
  included: number().int().nonnegative().meta(readingMeta("count", {
@@ -58730,7 +59087,7 @@ object({
58730
59087
  label: "Members"
58731
59088
  }))
58732
59089
  });
58733
- DeviceType.Sensor, DeviceType.Switch, DeviceType.Light;
59090
+ DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
58734
59091
  object({
58735
59092
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
58736
59093
  position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -59637,7 +59994,14 @@ DeviceType.Lock, method(object({
59637
59994
  auth: "protected"
59638
59995
  }), method(object({
59639
59996
  deviceId: number().int().nonnegative(),
59640
- code: string().min(1).optional()
59997
+ code: string().min(1).optional(),
59998
+ /**
59999
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
60000
+ * with one call is refused unless the caller says it was confirmed. A
60001
+ * single lock ignores it. The refusal lives in the callee, so no
60002
+ * automation or API caller reaches the doors by skipping a dialog.
60003
+ */
60004
+ confirm: boolean().optional()
59641
60005
  }), _void(), {
59642
60006
  kind: "mutation",
59643
60007
  auth: "protected"
@@ -59645,6 +60009,11 @@ DeviceType.Lock, method(object({
59645
60009
  kind: "mutation",
59646
60010
  auth: "protected"
59647
60011
  });
60012
+ object({
60013
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
60014
+ lastFetchedAt: number()
60015
+ });
60016
+ DeviceType.Sensor;
59648
60017
  /**
59649
60018
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
59650
60019
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
@@ -61552,9 +61921,9 @@ object({
61552
61921
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
61553
61922
  kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
61554
61923
  /** Voltage in volts. */
61555
- volts: number().optional().meta(readingMeta("voltage")),
61924
+ volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
61556
61925
  /** Current in amperes. */
61557
- amps: number().optional().meta(readingMeta("current")),
61926
+ amps: number().optional().meta(readingMeta("current", { label: "Current" })),
61558
61927
  /** Apparent power in volt-amperes. */
61559
61928
  va: number().optional().meta(readingMeta("apparent-power")),
61560
61929
  /** Power factor (0..1) if reported. */
@@ -61562,7 +61931,23 @@ object({
61562
61931
  /** Ms epoch when the slice was last updated. */
61563
61932
  lastFetchedAt: number()
61564
61933
  });
61565
- DeviceType.Sensor;
61934
+ [
61935
+ DeviceType.Sensor,
61936
+ DeviceType.Switch,
61937
+ DeviceType.Light,
61938
+ DeviceType.Fan,
61939
+ DeviceType.Climate,
61940
+ DeviceType.Thermostat,
61941
+ DeviceType.Humidifier,
61942
+ DeviceType.WaterHeater,
61943
+ DeviceType.MediaPlayer,
61944
+ DeviceType.Washer,
61945
+ DeviceType.Dryer,
61946
+ DeviceType.Dishwasher,
61947
+ DeviceType.Vacuum,
61948
+ DeviceType.LawnMower,
61949
+ DeviceType.PetFeeder
61950
+ ].filter((type) => type !== DeviceType.Sensor);
61566
61951
  /**
61567
61952
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
61568
61953
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -62009,7 +62394,7 @@ object({
62009
62394
  phase: string().nullable()
62010
62395
  });
62011
62396
  Object.values(DeviceType);
62012
- DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
62397
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
62013
62398
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
62014
62399
  kind: "mutation",
62015
62400
  auth: "admin"
@@ -64048,7 +64433,7 @@ object({
64048
64433
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
64049
64434
  lastChangedAt: number()
64050
64435
  });
64051
- DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
64436
+ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
64052
64437
  deviceId: number(),
64053
64438
  on: boolean()
64054
64439
  }), _void(), {
@@ -65740,8 +66125,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
65740
66125
  kind: "mutation",
65741
66126
  auth: "admin"
65742
66127
  });
66128
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
65743
66129
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
65744
- 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;
66130
+ 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;
65745
66131
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
65746
66132
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
65747
66133
  new Set(CAP_NAMES);
@@ -65794,7 +66180,7 @@ var AccessoryKind = {
65794
66180
  PrivacyMask: DeviceRole.PrivacyMask
65795
66181
  };
65796
66182
  AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
65797
- 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;
66183
+ 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;
65798
66184
  new Set(Object.values(DeviceType));
65799
66185
  Object.freeze({
65800
66186
  "accessories.getStatus": {
@@ -69017,6 +69403,13 @@ Object.freeze({
69017
69403
  access: "view",
69018
69404
  role: "viewer"
69019
69405
  },
69406
+ "groupActive.getStatus": {
69407
+ capName: "group-active",
69408
+ capScope: "device",
69409
+ addonId: null,
69410
+ access: "view",
69411
+ role: "viewer"
69412
+ },
69020
69413
  "groupCommands.getStatus": {
69021
69414
  capName: "group-commands",
69022
69415
  capScope: "device",
@@ -69983,6 +70376,13 @@ Object.freeze({
69983
70376
  access: "create",
69984
70377
  role: "admin"
69985
70378
  },
70379
+ "meter.getStatus": {
70380
+ capName: "meter",
70381
+ capScope: "device",
70382
+ addonId: null,
70383
+ access: "view",
70384
+ role: "viewer"
70385
+ },
69986
70386
  "metricsProvider.collectSnapshot": {
69987
70387
  capName: "metrics-provider",
69988
70388
  capScope: "system",
@@ -78081,7 +78481,39 @@ var APPLIANCE_CAP_VOCABULARY = {
78081
78481
  phrase: t("When {device} finishes", "Quando {device} finisce"),
78082
78482
  icon: "square"
78083
78483
  }
78084
- }
78484
+ },
78485
+ overrides: { byType: {
78486
+ [DeviceType.Washer]: {
78487
+ state: { running: {
78488
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
78489
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
78490
+ } },
78491
+ events: {
78492
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
78493
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
78494
+ }
78495
+ },
78496
+ [DeviceType.Dryer]: {
78497
+ state: { running: {
78498
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
78499
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
78500
+ } },
78501
+ events: {
78502
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
78503
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
78504
+ }
78505
+ },
78506
+ [DeviceType.Dishwasher]: {
78507
+ state: { running: {
78508
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
78509
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
78510
+ } },
78511
+ events: {
78512
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
78513
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
78514
+ }
78515
+ }
78516
+ } }
78085
78517
  },
78086
78518
  update: {
78087
78519
  scope: "device",
@@ -78853,6 +79285,10 @@ var SENSOR_CAP_VOCABULARY = {
78853
79285
  icon: "flame"
78854
79286
  }]
78855
79287
  },
79288
+ "group-active": {
79289
+ scope: "device",
79290
+ noun: t("Group state", "Stato del gruppo")
79291
+ },
78856
79292
  "group-commands": {
78857
79293
  scope: "device",
78858
79294
  noun: t("Group command", "Comando di gruppo")
@@ -78909,6 +79345,17 @@ var SENSOR_CAP_VOCABULARY = {
78909
79345
  }],
78910
79346
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
78911
79347
  },
79348
+ meter: {
79349
+ scope: "device",
79350
+ noun: t("Meter", "Contatore"),
79351
+ state: [{
79352
+ kind: "numeric",
79353
+ field: "value",
79354
+ quantity: t("the total", "il totale"),
79355
+ label: t("Total", "Totale"),
79356
+ icon: "gauge"
79357
+ }]
79358
+ },
78912
79359
  "numeric-sensor": {
78913
79360
  scope: "device",
78914
79361
  noun: t("Sensor", "Sensore"),
@@ -79381,8 +79828,47 @@ function projectAction(cap, action) {
79381
79828
  };
79382
79829
  }
79383
79830
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
79384
- 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;
79831
+ 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;
79385
79832
  DeviceFeature.BatteryOperated;
79833
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
79834
+ 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.";
79835
+ 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.";
79836
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
79837
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
79838
+ 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.";
79839
+ function withheld(reason) {
79840
+ return {
79841
+ [DeviceType.Washer]: {
79842
+ exported: false,
79843
+ reason
79844
+ },
79845
+ [DeviceType.Dryer]: {
79846
+ exported: false,
79847
+ reason
79848
+ },
79849
+ [DeviceType.Dishwasher]: {
79850
+ exported: false,
79851
+ reason
79852
+ }
79853
+ };
79854
+ }
79855
+ function offered(received) {
79856
+ return {
79857
+ [DeviceType.Washer]: {
79858
+ exported: true,
79859
+ as: received
79860
+ },
79861
+ [DeviceType.Dryer]: {
79862
+ exported: true,
79863
+ as: received
79864
+ },
79865
+ [DeviceType.Dishwasher]: {
79866
+ exported: true,
79867
+ as: received
79868
+ }
79869
+ };
79870
+ }
79871
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
79386
79872
  Object.freeze({
79387
79873
  "accessories.getStatus": [{
79388
79874
  name: "deviceId",
@@ -80349,6 +80835,11 @@ Object.freeze({
80349
80835
  form: "single",
80350
80836
  optional: false
80351
80837
  }],
80838
+ "groupActive.getStatus": [{
80839
+ name: "deviceId",
80840
+ form: "single",
80841
+ optional: false
80842
+ }],
80352
80843
  "groupCommands.getStatus": [{
80353
80844
  name: "deviceId",
80354
80845
  form: "single",
@@ -80559,6 +81050,11 @@ Object.freeze({
80559
81050
  form: "single",
80560
81051
  optional: false
80561
81052
  }],
81053
+ "meter.getStatus": [{
81054
+ name: "deviceId",
81055
+ form: "single",
81056
+ optional: false
81057
+ }],
80562
81058
  "motion.getStatus": [{
80563
81059
  name: "deviceId",
80564
81060
  form: "single",
@@ -82166,7 +82662,6 @@ Object.freeze({
82166
82662
  "network-access": "ingress",
82167
82663
  "smtp-provider": "email"
82168
82664
  });
82169
- DeviceType.Camera;
82170
82665
  /**
82171
82666
  * A saved Customize assignment (D936). The schema is the type: callers use
82172
82667
  * `z.infer`, they do not restate the shape.
@@ -83915,7 +84410,7 @@ function buildEmailDescriptor(providers = []) {
83915
84410
  id: "email",
83916
84411
  title: "Email",
83917
84412
  titleKey: "notifiers:emailDescriptor.s_email.title",
83918
- description: "Send via a Connectivity → Email provider (Google Services or SMTP). Install and Verify a provider first.",
84413
+ description: "Send via a Connectivity → Email provider (Google or Email (SMTP)). Install and Verify a provider first.",
83919
84414
  descriptionKey: "notifiers:emailDescriptor.s_email.description",
83920
84415
  fields: [
83921
84416
  {
@@ -83932,7 +84427,7 @@ function buildEmailDescriptor(providers = []) {
83932
84427
  key: "smtpAddonId",
83933
84428
  label: "Email provider",
83934
84429
  labelKey: "notifiers:emailDescriptor.s_email.smtpAddonId.label",
83935
- description: options.length === 0 ? "No Email provider installed. Open Connectivity → Email, install Google Services or SMTP, then Verify." : "Which Connectivity → Email entry delivers this target. Verify it under Connectivity → Email if the label says not verified.",
84430
+ description: options.length === 0 ? "No Email provider installed. Open Connectivity → Email, install Google or Email (SMTP), then Verify." : "Which Connectivity → Email entry delivers this target. Verify it under Connectivity → Email if the label says not verified.",
83936
84431
  required: true,
83937
84432
  options
83938
84433
  },
@@ -86232,7 +86727,7 @@ var HTTP_NOTIFIER_ADAPTERS = [
86232
86727
  */
86233
86728
  function buildNotifierAdapters(smtp) {
86234
86729
  const emailSmtp = smtp ?? { async sendEmail() {
86235
- throw new Error("email: no smtp-provider client — install Google Services or SMTP Email under Connectivity → Email and Verify it");
86730
+ throw new Error("email: no smtp-provider client — install Google or Email (SMTP) under Connectivity → Email and Verify it");
86236
86731
  } };
86237
86732
  return [...HTTP_NOTIFIER_ADAPTERS, createEmailAdapter(emailSmtp)];
86238
86733
  }