@camstack/addon-pipeline-orchestrator 1.2.630 → 1.2.632

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- //#region ../types/dist/event-category-DHXxB8t9.mjs
5
+ //#region ../types/dist/event-category-CT-hz5Rb.mjs
6
6
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
7
7
  EventCategory["SystemBoot"] = "system.boot";
8
8
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -328,6 +328,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
328
328
  /** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
329
329
  EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
330
330
  /**
331
+ * A camera's package LLM confirm has failed closed three times in a row
332
+ * (D931, 2026-10-26). One edge per camera until
333
+ * {@link PipelinePackageLlmAnswered}.
334
+ */
335
+ EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
336
+ /** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
337
+ EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
338
+ /**
331
339
  * Per-camera pipeline config was mutated by the orchestrator
332
340
  * (a settings change via `setAgentInferenceDevices` /
333
341
  * `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
@@ -5570,7 +5578,7 @@ var ZodIssueCode = {
5570
5578
  var ZodFirstPartyTypeKind;
5571
5579
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5572
5580
  //#endregion
5573
- //#region ../types/dist/sleep-B81gwQuR.mjs
5581
+ //#region ../types/dist/sleep--jXS9hWo.mjs
5574
5582
  /**
5575
5583
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5576
5584
  * window to float samples (D455).
@@ -9477,6 +9485,8 @@ var CAP_NAMES = Object.freeze([
9477
9485
  "filesystem-browse",
9478
9486
  "flood",
9479
9487
  "gas",
9488
+ "group-active",
9489
+ "group-commands",
9480
9490
  "group-status",
9481
9491
  "heating-valve",
9482
9492
  "humidifier",
@@ -9498,6 +9508,7 @@ var CAP_NAMES = Object.freeze([
9498
9508
  "login-method",
9499
9509
  "media-player",
9500
9510
  "mesh-network",
9511
+ "meter",
9501
9512
  "metrics-provider",
9502
9513
  "model-convert",
9503
9514
  "model-distributor",
@@ -10296,6 +10307,10 @@ Object.freeze({
10296
10307
  "path": "detected",
10297
10308
  "valueType": "boolean"
10298
10309
  }],
10310
+ "group-active": [{
10311
+ "path": "active",
10312
+ "valueType": "boolean"
10313
+ }],
10299
10314
  "group-status": [
10300
10315
  {
10301
10316
  "path": "included",
@@ -10555,6 +10570,10 @@ Object.freeze({
10555
10570
  ]
10556
10571
  }
10557
10572
  ],
10573
+ "meter": [{
10574
+ "path": "value",
10575
+ "valueType": "number"
10576
+ }],
10558
10577
  "motion": [
10559
10578
  {
10560
10579
  "path": "detected",
@@ -11322,7 +11341,7 @@ function sleep(ms) {
11322
11341
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11323
11342
  }
11324
11343
  //#endregion
11325
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
11344
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
11326
11345
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
11327
11346
  DeviceType["Camera"] = "camera";
11328
11347
  DeviceType["Hub"] = "hub";
@@ -11416,8 +11435,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
11416
11435
  DeviceType["PetFeeder"] = "pet-feeder";
11417
11436
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
11418
11437
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
11419
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
11438
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
11439
+ * A plug-built dishwasher carries running-state and power instead. */
11420
11440
  DeviceType["Dishwasher"] = "dishwasher";
11441
+ /** Washing machine. A plug-built one is running while its power stays
11442
+ * above a threshold. No program cap — that surface is the dishwasher's. */
11443
+ DeviceType["Washer"] = "washer";
11444
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
11445
+ DeviceType["Dryer"] = "dryer";
11421
11446
  return DeviceType;
11422
11447
  }({});
11423
11448
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11675,7 +11700,7 @@ function errMsg$2(err) {
11675
11700
  return String(err);
11676
11701
  }
11677
11702
  //#endregion
11678
- //#region ../types/dist/composition-B85l9j1M.mjs
11703
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
11679
11704
  /**
11680
11705
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11681
11706
  *
@@ -12119,6 +12144,13 @@ var table = {
12119
12144
  apply: () => {
12120
12145
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
12121
12146
  }
12147
+ },
12148
+ schmitt: {
12149
+ minArgs: 3,
12150
+ maxArgs: 5,
12151
+ apply: () => {
12152
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
12153
+ }
12122
12154
  }
12123
12155
  };
12124
12156
  Object.freeze(Object.assign(Object.create(null), table));
@@ -12560,6 +12592,99 @@ function validateExpressionSource(src) {
12560
12592
  return null;
12561
12593
  }
12562
12594
  /**
12595
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
12596
+ *
12597
+ * Standby of the control board is under 5 W; a motor or a heater is far
12598
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
12599
+ * a cycle, so running starts only after 30 s above the line.
12600
+ *
12601
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
12602
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
12603
+ * report "finished" in the middle of the program, so the stop dwell is
12604
+ * 10 minutes on every type. The number is stored per type so one machine
12605
+ * can move later without moving the others.
12606
+ */
12607
+ var APPLIANCE_TEMPLATE_ID = "appliance";
12608
+ var APPLIANCE_KINDS = [
12609
+ "washer",
12610
+ "dryer",
12611
+ "dishwasher"
12612
+ ];
12613
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
12614
+ /**
12615
+ * Statistics and integrals over a history read (D944).
12616
+ *
12617
+ * Pure: the same points always give the same answer, so a restart recomputes
12618
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
12619
+ * never the mean of the samples. A hole with no held value is not integrated
12620
+ * as zero. A truncated read is not published.
12621
+ */
12622
+ var STATISTIC_STATS = [
12623
+ "min",
12624
+ "max",
12625
+ "mean",
12626
+ "sum",
12627
+ "count",
12628
+ "change"
12629
+ ];
12630
+ /**
12631
+ * The range a statistics or integral helper asks history for (D944).
12632
+ *
12633
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
12634
+ * or 25 hours across a DST change. A tier is eligible only when its retention
12635
+ * is strictly longer than the longest that range can be. Forever qualifies.
12636
+ * Equal to a tier's retention is a refusal, not a short answer.
12637
+ */
12638
+ var MINUTE_MS = 6e4;
12639
+ var HOUR_MS = 60 * MINUTE_MS;
12640
+ var DAY_MS = 24 * HOUR_MS;
12641
+ var ROLLING_SPANS = [
12642
+ "1h",
12643
+ "24h",
12644
+ "2d",
12645
+ "7d",
12646
+ "30d",
12647
+ "custom"
12648
+ ];
12649
+ var CALENDAR_PERIODS = [
12650
+ "today",
12651
+ "yesterday",
12652
+ "this-week",
12653
+ "this-month"
12654
+ ];
12655
+ var RESET_PERIODS = [
12656
+ "never",
12657
+ "daily",
12658
+ "weekly",
12659
+ "monthly"
12660
+ ];
12661
+ 5 * MINUTE_MS;
12662
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
12663
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
12664
+ var rollingWindowObject = object({
12665
+ kind: literal("rolling"),
12666
+ span: _enum(ROLLING_SPANS),
12667
+ customMinutes: number().int().min(5).optional()
12668
+ }).check((payload) => {
12669
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
12670
+ code: "custom",
12671
+ message: "a custom range needs a duration in minutes",
12672
+ path: ["customMinutes"],
12673
+ input: payload.value
12674
+ });
12675
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
12676
+ code: "custom",
12677
+ message: "only a custom range carries a duration",
12678
+ path: ["customMinutes"],
12679
+ input: payload.value
12680
+ });
12681
+ });
12682
+ var RollingWindowSchema = rollingWindowObject;
12683
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
12684
+ kind: literal("calendar"),
12685
+ period: _enum(CALENDAR_PERIODS)
12686
+ })]);
12687
+ /**
12563
12688
  * A composition source identity: `(addonId, stableId)`, never a numeric device
12564
12689
  * id. Split out so helper schemas can name it without a cycle through
12565
12690
  * `composition.ts` (D659).
@@ -12583,7 +12708,15 @@ var GROUP_TEMPLATE_IDS = [
12583
12708
  "leak",
12584
12709
  "smoke",
12585
12710
  "occupancy",
12586
- "binary"
12711
+ "binary",
12712
+ "switch",
12713
+ "light",
12714
+ "fan",
12715
+ "cover",
12716
+ "valve",
12717
+ "lock",
12718
+ "media-player",
12719
+ "climate"
12587
12720
  ];
12588
12721
  var NUMERIC_AGGREGATES = [
12589
12722
  "mean",
@@ -12593,6 +12726,8 @@ var NUMERIC_AGGREGATES = [
12593
12726
  "sum"
12594
12727
  ];
12595
12728
  var BINARY_AGGREGATES = ["any", "all"];
12729
+ /** How an actuator group decides it is on. Unknown members never count as off. */
12730
+ var ACTIVE_CRITERIA = ["at-least-one", "all"];
12596
12731
  var AGGREGATE_REDUCERS = [
12597
12732
  "mean",
12598
12733
  "median",
@@ -12604,18 +12739,80 @@ var AGGREGATE_REDUCERS = [
12604
12739
  ];
12605
12740
  var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
12606
12741
  var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
12742
+ var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
12607
12743
  /** One member. The field is stored so occupancy can name motion or binary. */
12608
12744
  var HelperMemberSchema = object({
12609
12745
  source: CompositionSourceRefSchema,
12610
12746
  cap: string().min(1),
12611
12747
  fieldPath: string().min(1)
12612
12748
  });
12613
- var HelperSpecSchema = discriminatedUnion("kind", [object({
12614
- kind: literal("group"),
12615
- templateId: GroupTemplateIdSchema,
12616
- aggregate: AggregateReducerSchema,
12617
- members: array(HelperMemberSchema).min(1).max(64)
12618
- })]);
12749
+ var HelperSpecSchema = discriminatedUnion("kind", [
12750
+ object({
12751
+ kind: literal("group"),
12752
+ templateId: GroupTemplateIdSchema,
12753
+ aggregate: AggregateReducerSchema,
12754
+ members: array(HelperMemberSchema).min(1).max(64),
12755
+ /** Actuator groups only. Readings leave this unset. */
12756
+ active: ActiveCriterionSchema.optional()
12757
+ }),
12758
+ object({
12759
+ kind: literal("appliance"),
12760
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
12761
+ appliance: _enum(APPLIANCE_KINDS),
12762
+ thresholdWatts: number().positive(),
12763
+ startDwellMs: number().int().nonnegative(),
12764
+ stopDwellMs: number().int().nonnegative(),
12765
+ member: HelperMemberSchema
12766
+ }),
12767
+ object({
12768
+ kind: literal("statistic"),
12769
+ stat: _enum(STATISTIC_STATS),
12770
+ window: StatisticWindowSchema,
12771
+ source: HelperMemberSchema
12772
+ }),
12773
+ object({
12774
+ kind: literal("integral"),
12775
+ reset: _enum(RESET_PERIODS),
12776
+ output: _enum(["energy", "meter"]),
12777
+ source: HelperMemberSchema
12778
+ }),
12779
+ object({
12780
+ kind: literal("threshold"),
12781
+ source: HelperMemberSchema,
12782
+ high: number(),
12783
+ low: number(),
12784
+ onForMs: number().int().nonnegative(),
12785
+ offForMs: number().int().nonnegative()
12786
+ }),
12787
+ object({
12788
+ kind: literal("arithmetic"),
12789
+ op: _enum([
12790
+ "sum",
12791
+ "difference",
12792
+ "ratio"
12793
+ ]),
12794
+ inputs: array(HelperMemberSchema).min(2).max(64)
12795
+ }),
12796
+ object({
12797
+ kind: literal("expression"),
12798
+ expr: string().min(1),
12799
+ inputs: array(object({
12800
+ name: string().min(1),
12801
+ member: HelperMemberSchema
12802
+ })).min(1).max(64),
12803
+ output: _enum(["number", "boolean"])
12804
+ }),
12805
+ object({
12806
+ kind: literal("derivative"),
12807
+ window: RollingWindowSchema,
12808
+ per: _enum([
12809
+ "second",
12810
+ "minute",
12811
+ "hour"
12812
+ ]),
12813
+ source: HelperMemberSchema
12814
+ })
12815
+ ]);
12619
12816
  /**
12620
12817
  * A COMPOSITION: a device assembled from other devices' state (D659).
12621
12818
  *
@@ -12697,6 +12894,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12697
12894
  "all"
12698
12895
  ]),
12699
12896
  reads: array(CompositionFieldReadSchema).min(1).max(64),
12897
+ /**
12898
+ * Set when the reads are an enum state, not a yes/no: the listed states
12899
+ * count as active (a cover is `open`). A state outside the list is inactive;
12900
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
12901
+ */
12902
+ activeWhen: array(string().min(1)).min(1).optional(),
12700
12903
  /** `value` is the reading; the others are the member counts. */
12701
12904
  publish: _enum([
12702
12905
  "value",
@@ -12710,6 +12913,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12710
12913
  "omit",
12711
12914
  "offline"
12712
12915
  ])
12916
+ }),
12917
+ object({
12918
+ kind: literal("series"),
12919
+ read: CompositionFieldReadSchema,
12920
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12921
+ publish: _enum([
12922
+ "value",
12923
+ "included",
12924
+ "excluded",
12925
+ "total"
12926
+ ]),
12927
+ /**
12928
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12929
+ * device unseeded until history answers, so a restart cannot publish 0.
12930
+ */
12931
+ empty: _enum([
12932
+ "null",
12933
+ "omit",
12934
+ "offline"
12935
+ ])
12713
12936
  })
12714
12937
  ]);
12715
12938
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -18218,7 +18441,22 @@ var BrokerProviderInfoSchema = object({
18218
18441
  */
18219
18442
  interactiveConnectPath: string().min(1).optional(),
18220
18443
  /** Relative hub path to disconnect the linked account (same popup pattern). */
18221
- interactiveDisconnectPath: string().min(1).optional()
18444
+ interactiveDisconnectPath: string().min(1).optional(),
18445
+ /**
18446
+ * The kind signs in through a server-held interactive session instead of
18447
+ * (or before) typed credentials. `basePath` is a hub-relative route set
18448
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
18449
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
18450
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
18451
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
18452
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
18453
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
18454
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
18455
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
18456
+ * offers it as the default path; the fields the session fills are
18457
+ * marked `filledBySignIn` in the kind's settings schema.
18458
+ */
18459
+ signIn: object({ basePath: string().min(1) }).optional()
18222
18460
  }))
18223
18461
  });
18224
18462
  var ListInputSchema = object({
@@ -19255,7 +19493,8 @@ var CompositionProblemSchema = object({
19255
19493
  "native-capability-replace",
19256
19494
  "mixed-quantity",
19257
19495
  "helper-mismatch",
19258
- "member-constraint"
19496
+ "member-constraint",
19497
+ "retention"
19259
19498
  ]),
19260
19499
  path: string(),
19261
19500
  message: string()
@@ -19489,13 +19728,17 @@ Object.freeze({
19489
19728
  latchedAt: {
19490
19729
  kind: "fixed",
19491
19730
  result: ["number", "null"]
19731
+ },
19732
+ schmitt: {
19733
+ kind: "fixed",
19734
+ result: ["boolean", "null"]
19492
19735
  }
19493
19736
  });
19494
19737
  /**
19495
19738
  * One catalog row per base reading a group helper can build (D944 §3).
19496
19739
  * Data, not a form default written into the block.
19497
19740
  */
19498
- var NOW = {
19741
+ var NOW$2 = {
19499
19742
  fieldPath: "lastFetchedAt",
19500
19743
  expr: "now"
19501
19744
  };
@@ -19509,6 +19752,9 @@ function numeric(input) {
19509
19752
  family: "numeric",
19510
19753
  deviceType: DeviceType.Sensor,
19511
19754
  memberDeviceType: DeviceType.Sensor,
19755
+ memberDeviceTypes: [DeviceType.Sensor],
19756
+ activeWhen: void 0,
19757
+ commandCaps: [],
19512
19758
  baseUnit: baseUnitOf(input.quantity),
19513
19759
  aggregates: NUMERIC_AGGREGATES,
19514
19760
  accept: [{
@@ -19523,6 +19769,9 @@ function binary(input) {
19523
19769
  family: "binary",
19524
19770
  deviceType: DeviceType.Sensor,
19525
19771
  memberDeviceType: DeviceType.Sensor,
19772
+ memberDeviceTypes: [DeviceType.Sensor],
19773
+ activeWhen: void 0,
19774
+ commandCaps: [],
19526
19775
  quantity: "none",
19527
19776
  baseUnit: void 0,
19528
19777
  aggregates: BINARY_AGGREGATES,
@@ -19539,7 +19788,7 @@ var GROUP_TEMPLATES = [
19539
19788
  quantity: "temperature",
19540
19789
  defaultAggregate: "mean",
19541
19790
  empty: "null",
19542
- companions: [NOW]
19791
+ companions: [NOW$2]
19543
19792
  }),
19544
19793
  numeric({
19545
19794
  id: "humidity",
@@ -19550,7 +19799,7 @@ var GROUP_TEMPLATES = [
19550
19799
  quantity: "humidity",
19551
19800
  defaultAggregate: "mean",
19552
19801
  empty: "null",
19553
- companions: [NOW]
19802
+ companions: [NOW$2]
19554
19803
  }),
19555
19804
  numeric({
19556
19805
  id: "pressure",
@@ -19561,7 +19810,7 @@ var GROUP_TEMPLATES = [
19561
19810
  quantity: "pressure",
19562
19811
  defaultAggregate: "mean",
19563
19812
  empty: "null",
19564
- companions: [NOW]
19813
+ companions: [NOW$2]
19565
19814
  }),
19566
19815
  numeric({
19567
19816
  id: "illuminance",
@@ -19572,7 +19821,7 @@ var GROUP_TEMPLATES = [
19572
19821
  quantity: "illuminance",
19573
19822
  defaultAggregate: "mean",
19574
19823
  empty: "null",
19575
- companions: [NOW]
19824
+ companions: [NOW$2]
19576
19825
  }),
19577
19826
  numeric({
19578
19827
  id: "power",
@@ -19583,7 +19832,7 @@ var GROUP_TEMPLATES = [
19583
19832
  quantity: "power",
19584
19833
  defaultAggregate: "sum",
19585
19834
  empty: "omit",
19586
- companions: [NOW]
19835
+ companions: [NOW$2]
19587
19836
  }),
19588
19837
  numeric({
19589
19838
  id: "energy",
@@ -19594,7 +19843,7 @@ var GROUP_TEMPLATES = [
19594
19843
  quantity: "energy",
19595
19844
  defaultAggregate: "sum",
19596
19845
  empty: "omit",
19597
- companions: [NOW]
19846
+ companions: [NOW$2]
19598
19847
  }),
19599
19848
  numeric({
19600
19849
  id: "battery",
@@ -19629,7 +19878,7 @@ var GROUP_TEMPLATES = [
19629
19878
  quantity: "device",
19630
19879
  defaultAggregate: "mean",
19631
19880
  empty: "null",
19632
- companions: [NOW]
19881
+ companions: [NOW$2]
19633
19882
  }),
19634
19883
  binary({
19635
19884
  id: "motion",
@@ -19717,9 +19966,154 @@ var GROUP_TEMPLATES = [
19717
19966
  fieldPath: "on"
19718
19967
  }],
19719
19968
  companions: [CHANGED]
19969
+ }),
19970
+ actuator({
19971
+ id: "switch",
19972
+ 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.",
19973
+ deviceType: DeviceType.Switch,
19974
+ outputCap: "switch",
19975
+ outputField: "on",
19976
+ member: {
19977
+ cap: "switch",
19978
+ fieldPath: "on"
19979
+ },
19980
+ commandCaps: ["switch"]
19981
+ }),
19982
+ actuator({
19983
+ id: "light",
19984
+ 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.",
19985
+ deviceType: DeviceType.Light,
19986
+ outputCap: "switch",
19987
+ outputField: "on",
19988
+ member: {
19989
+ cap: "switch",
19990
+ fieldPath: "on"
19991
+ },
19992
+ commandCaps: [
19993
+ "switch",
19994
+ "brightness",
19995
+ "color"
19996
+ ]
19997
+ }),
19998
+ actuator({
19999
+ id: "fan",
20000
+ 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.",
20001
+ deviceType: DeviceType.Fan,
20002
+ outputCap: "switch",
20003
+ outputField: "on",
20004
+ member: {
20005
+ cap: "switch",
20006
+ fieldPath: "on"
20007
+ },
20008
+ commandCaps: ["switch", "fan-control"]
20009
+ }),
20010
+ actuator({
20011
+ id: "cover",
20012
+ 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.",
20013
+ deviceType: DeviceType.Cover,
20014
+ outputCap: "group-active",
20015
+ outputField: "active",
20016
+ member: {
20017
+ cap: "cover",
20018
+ fieldPath: "state"
20019
+ },
20020
+ activeWhen: [
20021
+ "open",
20022
+ "opening",
20023
+ "stopped"
20024
+ ],
20025
+ commandCaps: ["cover"]
20026
+ }),
20027
+ actuator({
20028
+ id: "valve",
20029
+ 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.",
20030
+ deviceType: DeviceType.Valve,
20031
+ outputCap: "group-active",
20032
+ outputField: "active",
20033
+ member: {
20034
+ cap: "valve",
20035
+ fieldPath: "state"
20036
+ },
20037
+ activeWhen: [
20038
+ "open",
20039
+ "opening",
20040
+ "stopped"
20041
+ ],
20042
+ commandCaps: ["valve"]
20043
+ }),
20044
+ actuator({
20045
+ id: "lock",
20046
+ 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.",
20047
+ deviceType: DeviceType.Lock,
20048
+ outputCap: "group-active",
20049
+ outputField: "active",
20050
+ member: {
20051
+ cap: "lock-control",
20052
+ fieldPath: "state"
20053
+ },
20054
+ activeWhen: ["locked"],
20055
+ commandCaps: ["lock-control"]
20056
+ }),
20057
+ actuator({
20058
+ id: "media-player",
20059
+ 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.",
20060
+ deviceType: DeviceType.MediaPlayer,
20061
+ outputCap: "group-active",
20062
+ outputField: "active",
20063
+ member: {
20064
+ cap: "media-player",
20065
+ fieldPath: "state"
20066
+ },
20067
+ activeWhen: ["playing", "buffering"],
20068
+ commandCaps: ["media-player"]
20069
+ }),
20070
+ actuator({
20071
+ id: "climate",
20072
+ 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.",
20073
+ deviceType: DeviceType.Thermostat,
20074
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
20075
+ outputCap: "group-active",
20076
+ outputField: "active",
20077
+ member: {
20078
+ cap: "climate-control",
20079
+ fieldPath: "mode"
20080
+ },
20081
+ activeWhen: [
20082
+ "heat",
20083
+ "cool",
20084
+ "auto",
20085
+ "heat_cool",
20086
+ "fan_only",
20087
+ "dry"
20088
+ ],
20089
+ commandCaps: ["climate-control"]
19720
20090
  })
19721
20091
  ];
20092
+ function actuator(input) {
20093
+ return {
20094
+ id: input.id,
20095
+ summary: input.summary,
20096
+ deviceType: input.deviceType,
20097
+ outputCap: input.outputCap,
20098
+ outputField: input.outputField,
20099
+ commandCaps: input.commandCaps,
20100
+ family: "actuator",
20101
+ role: void 0,
20102
+ memberDeviceType: input.deviceType,
20103
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
20104
+ activeWhen: input.activeWhen,
20105
+ quantity: "none",
20106
+ baseUnit: void 0,
20107
+ aggregates: BINARY_AGGREGATES,
20108
+ defaultAggregate: "any",
20109
+ empty: "offline",
20110
+ accept: [input.member],
20111
+ companions: [CHANGED]
20112
+ };
20113
+ }
19722
20114
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
20115
+ DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
20116
+ DeviceType.Camera;
19723
20117
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19724
20118
  /**
19725
20119
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -44734,7 +45128,7 @@ var NetworkAccessStatusSchema = object({
44734
45128
  });
44735
45129
  /**
44736
45130
  * Optional, richer endpoint shape returned by providers that expose
44737
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
45131
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44738
45132
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44739
45133
  * the originating provider config (mode + sourcePort) so the
44740
45134
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -46554,6 +46948,8 @@ var NcSystemEventKindSchema = _enum([
46554
46948
  "detection-blind",
46555
46949
  "motion-noisy",
46556
46950
  "motion-calm",
46951
+ "package-llm-silent",
46952
+ "package-llm-answered",
46557
46953
  "recording-stalled",
46558
46954
  "recording-healthy",
46559
46955
  "recording-degraded",
@@ -47643,7 +48039,20 @@ var NcRuleInputSchema = object({
47643
48039
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47644
48040
  * path does not run Zod defaults.
47645
48041
  */
47646
- groupingSec: number().int().min(5).max(60).optional()
48042
+ groupingSec: number().int().min(5).max(60).optional(),
48043
+ /**
48044
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
48045
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
48046
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
48047
+ * same mark the Library writes, and records whose notification it was and
48048
+ * when. A channel with no buttons gets the same two choices as links.
48049
+ *
48050
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
48051
+ * cap path): absent and `false` both mean off, which is what every rule
48052
+ * authored before this field meant. An editor turns it off by writing
48053
+ * `false`, so it needs no `clear` key.
48054
+ */
48055
+ askVerdict: boolean().optional()
47647
48056
  });
47648
48057
  /**
47649
48058
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -58491,6 +58900,28 @@ var PackageVersionInfoSchema = object({
58491
58900
  deprecated: string().optional(),
58492
58901
  distTags: array(string()).readonly()
58493
58902
  });
58903
+ /**
58904
+ * One published document. `ok` is markdown from the registry. `missing` means
58905
+ * the registry answered and the file was not there. `error` is a registry or
58906
+ * tarball failure — never an empty string standing in for either.
58907
+ */
58908
+ var PackageDocBodySchema = discriminatedUnion("status", [
58909
+ object({
58910
+ status: literal("ok"),
58911
+ markdown: string()
58912
+ }),
58913
+ object({ status: literal("missing") }),
58914
+ object({
58915
+ status: literal("error"),
58916
+ message: string()
58917
+ })
58918
+ ]);
58919
+ var PackageDocsSchema = object({
58920
+ name: string(),
58921
+ version: string(),
58922
+ readme: PackageDocBodySchema,
58923
+ changelog: PackageDocBodySchema
58924
+ });
58494
58925
  var SearchResultSchema = object({
58495
58926
  name: string(),
58496
58927
  version: string(),
@@ -58709,7 +59140,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
58709
59140
  }), object({ success: literal(true) }), {
58710
59141
  kind: "mutation",
58711
59142
  auth: "admin"
58712
- }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
59143
+ }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
59144
+ name: string().min(1),
59145
+ version: string().min(1).optional()
59146
+ }), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
58713
59147
  kind: "mutation",
58714
59148
  auth: "admin"
58715
59149
  }), method(object({ packageName: string() }), object({ success: literal(true) }), {
@@ -58998,7 +59432,7 @@ var BatteryStatusSchema = object({
58998
59432
  */
58999
59433
  binary: boolean().optional().meta({ label: "Binary indicator" })
59000
59434
  });
59001
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat;
59435
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter;
59002
59436
  method(object({
59003
59437
  deviceId: number(),
59004
59438
  /** Bound on the wait. Sensible range 3000–10000ms. */
@@ -60740,6 +61174,24 @@ object({
60740
61174
  lastChangedAt: number()
60741
61175
  });
60742
61176
  DeviceType.Sensor;
61177
+ object({
61178
+ /** One row per member that has been commanded. A later command replaces that member's row. */
61179
+ results: array(object({
61180
+ deviceId: number().int().nonnegative(),
61181
+ cap: string().min(1),
61182
+ method: string().min(1),
61183
+ ok: boolean(),
61184
+ error: string().optional(),
61185
+ /** Ms epoch of the attempt. */
61186
+ at: number()
61187
+ })) });
61188
+ DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
61189
+ object({
61190
+ active: boolean().meta({ label: "Active" }),
61191
+ /** Ms epoch when the slice was last updated. */
61192
+ lastChangedAt: number()
61193
+ });
61194
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
60743
61195
  object({
60744
61196
  /** Members whose current value entered the aggregate. */
60745
61197
  included: number().int().nonnegative().meta(readingMeta("count", {
@@ -60757,7 +61209,7 @@ object({
60757
61209
  label: "Members"
60758
61210
  }))
60759
61211
  });
60760
- DeviceType.Sensor;
61212
+ DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
60761
61213
  object({
60762
61214
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
60763
61215
  position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -61664,7 +62116,14 @@ DeviceType.Lock, method(object({
61664
62116
  auth: "protected"
61665
62117
  }), method(object({
61666
62118
  deviceId: number().int().nonnegative(),
61667
- code: string().min(1).optional()
62119
+ code: string().min(1).optional(),
62120
+ /**
62121
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62122
+ * with one call is refused unless the caller says it was confirmed. A
62123
+ * single lock ignores it. The refusal lives in the callee, so no
62124
+ * automation or API caller reaches the doors by skipping a dialog.
62125
+ */
62126
+ confirm: boolean().optional()
61668
62127
  }), _void(), {
61669
62128
  kind: "mutation",
61670
62129
  auth: "protected"
@@ -61672,6 +62131,11 @@ DeviceType.Lock, method(object({
61672
62131
  kind: "mutation",
61673
62132
  auth: "protected"
61674
62133
  });
62134
+ object({
62135
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
62136
+ lastFetchedAt: number()
62137
+ });
62138
+ DeviceType.Sensor;
61675
62139
  /**
61676
62140
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
61677
62141
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
@@ -64320,9 +64784,9 @@ object({
64320
64784
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64321
64785
  kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64322
64786
  /** Voltage in volts. */
64323
- volts: number().optional().meta(readingMeta("voltage")),
64787
+ volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64324
64788
  /** Current in amperes. */
64325
- amps: number().optional().meta(readingMeta("current")),
64789
+ amps: number().optional().meta(readingMeta("current", { label: "Current" })),
64326
64790
  /** Apparent power in volt-amperes. */
64327
64791
  va: number().optional().meta(readingMeta("apparent-power")),
64328
64792
  /** Power factor (0..1) if reported. */
@@ -64330,7 +64794,23 @@ object({
64330
64794
  /** Ms epoch when the slice was last updated. */
64331
64795
  lastFetchedAt: number()
64332
64796
  });
64333
- DeviceType.Sensor;
64797
+ [
64798
+ DeviceType.Sensor,
64799
+ DeviceType.Switch,
64800
+ DeviceType.Light,
64801
+ DeviceType.Fan,
64802
+ DeviceType.Climate,
64803
+ DeviceType.Thermostat,
64804
+ DeviceType.Humidifier,
64805
+ DeviceType.WaterHeater,
64806
+ DeviceType.MediaPlayer,
64807
+ DeviceType.Washer,
64808
+ DeviceType.Dryer,
64809
+ DeviceType.Dishwasher,
64810
+ DeviceType.Vacuum,
64811
+ DeviceType.LawnMower,
64812
+ DeviceType.PetFeeder
64813
+ ].filter((type) => type !== DeviceType.Sensor);
64334
64814
  /**
64335
64815
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
64336
64816
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -64777,7 +65257,7 @@ object({
64777
65257
  phase: string().nullable()
64778
65258
  });
64779
65259
  Object.values(DeviceType);
64780
- DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
65260
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
64781
65261
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
64782
65262
  kind: "mutation",
64783
65263
  auth: "admin"
@@ -66850,7 +67330,7 @@ object({
66850
67330
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
66851
67331
  lastChangedAt: number()
66852
67332
  });
66853
- DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
67333
+ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
66854
67334
  deviceId: number(),
66855
67335
  on: boolean()
66856
67336
  }), _void(), {
@@ -68602,8 +69082,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
68602
69082
  kind: "mutation",
68603
69083
  auth: "admin"
68604
69084
  });
69085
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
68605
69086
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
68606
- 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;
69087
+ 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;
68607
69088
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
68608
69089
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
68609
69090
  new Set(CAP_NAMES);
@@ -68656,7 +69137,7 @@ var AccessoryKind = {
68656
69137
  PrivacyMask: DeviceRole.PrivacyMask
68657
69138
  };
68658
69139
  AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
68659
- 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;
69140
+ 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;
68660
69141
  new Set(Object.values(DeviceType));
68661
69142
  Object.freeze({
68662
69143
  "accessories.getStatus": {
@@ -68764,6 +69245,13 @@ Object.freeze({
68764
69245
  access: "view",
68765
69246
  role: "admin"
68766
69247
  },
69248
+ "addons.getPackageDocs": {
69249
+ capName: "addons",
69250
+ capScope: "system",
69251
+ addonId: null,
69252
+ access: "view",
69253
+ role: "admin"
69254
+ },
68767
69255
  "addons.getVersions": {
68768
69256
  capName: "addons",
68769
69257
  capScope: "system",
@@ -71872,6 +72360,20 @@ Object.freeze({
71872
72360
  access: "view",
71873
72361
  role: "viewer"
71874
72362
  },
72363
+ "groupActive.getStatus": {
72364
+ capName: "group-active",
72365
+ capScope: "device",
72366
+ addonId: null,
72367
+ access: "view",
72368
+ role: "viewer"
72369
+ },
72370
+ "groupCommands.getStatus": {
72371
+ capName: "group-commands",
72372
+ capScope: "device",
72373
+ addonId: null,
72374
+ access: "view",
72375
+ role: "viewer"
72376
+ },
71875
72377
  "groupStatus.getStatus": {
71876
72378
  capName: "group-status",
71877
72379
  capScope: "device",
@@ -72831,6 +73333,13 @@ Object.freeze({
72831
73333
  access: "create",
72832
73334
  role: "admin"
72833
73335
  },
73336
+ "meter.getStatus": {
73337
+ capName: "meter",
73338
+ capScope: "device",
73339
+ addonId: null,
73340
+ access: "view",
73341
+ role: "viewer"
73342
+ },
72834
73343
  "metricsProvider.collectSnapshot": {
72835
73344
  capName: "metrics-provider",
72836
73345
  capScope: "system",
@@ -80929,7 +81438,39 @@ var APPLIANCE_CAP_VOCABULARY = {
80929
81438
  phrase: t("When {device} finishes", "Quando {device} finisce"),
80930
81439
  icon: "square"
80931
81440
  }
80932
- }
81441
+ },
81442
+ overrides: { byType: {
81443
+ [DeviceType.Washer]: {
81444
+ state: { running: {
81445
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
81446
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
81447
+ } },
81448
+ events: {
81449
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
81450
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
81451
+ }
81452
+ },
81453
+ [DeviceType.Dryer]: {
81454
+ state: { running: {
81455
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
81456
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
81457
+ } },
81458
+ events: {
81459
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
81460
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
81461
+ }
81462
+ },
81463
+ [DeviceType.Dishwasher]: {
81464
+ state: { running: {
81465
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
81466
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
81467
+ } },
81468
+ events: {
81469
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
81470
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
81471
+ }
81472
+ }
81473
+ } }
80933
81474
  },
80934
81475
  update: {
80935
81476
  scope: "device",
@@ -81701,6 +82242,14 @@ var SENSOR_CAP_VOCABULARY = {
81701
82242
  icon: "flame"
81702
82243
  }]
81703
82244
  },
82245
+ "group-active": {
82246
+ scope: "device",
82247
+ noun: t("Group state", "Stato del gruppo")
82248
+ },
82249
+ "group-commands": {
82250
+ scope: "device",
82251
+ noun: t("Group command", "Comando di gruppo")
82252
+ },
81704
82253
  "group-status": {
81705
82254
  scope: "device",
81706
82255
  noun: t("Group", "Gruppo"),
@@ -81753,6 +82302,17 @@ var SENSOR_CAP_VOCABULARY = {
81753
82302
  }],
81754
82303
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
81755
82304
  },
82305
+ meter: {
82306
+ scope: "device",
82307
+ noun: t("Meter", "Contatore"),
82308
+ state: [{
82309
+ kind: "numeric",
82310
+ field: "value",
82311
+ quantity: t("the total", "il totale"),
82312
+ label: t("Total", "Totale"),
82313
+ icon: "gauge"
82314
+ }]
82315
+ },
81756
82316
  "numeric-sensor": {
81757
82317
  scope: "device",
81758
82318
  noun: t("Sensor", "Sensore"),
@@ -82225,7 +82785,7 @@ function projectAction(cap, action) {
82225
82785
  };
82226
82786
  }
82227
82787
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
82228
- 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;
82788
+ 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;
82229
82789
  /**
82230
82790
  * Heuristic check — `device.features?.includes(profile.when.hasFeature)`.
82231
82791
  */
@@ -82255,6 +82815,45 @@ function resolveDeviceProfile(features) {
82255
82815
  for (const profile of DEVICE_PROFILES) if (deviceMatchesProfile(features, profile)) return profile;
82256
82816
  return null;
82257
82817
  }
82818
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
82819
+ 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.";
82820
+ 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.";
82821
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
82822
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
82823
+ 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.";
82824
+ function withheld(reason) {
82825
+ return {
82826
+ [DeviceType.Washer]: {
82827
+ exported: false,
82828
+ reason
82829
+ },
82830
+ [DeviceType.Dryer]: {
82831
+ exported: false,
82832
+ reason
82833
+ },
82834
+ [DeviceType.Dishwasher]: {
82835
+ exported: false,
82836
+ reason
82837
+ }
82838
+ };
82839
+ }
82840
+ function offered(received) {
82841
+ return {
82842
+ [DeviceType.Washer]: {
82843
+ exported: true,
82844
+ as: received
82845
+ },
82846
+ [DeviceType.Dryer]: {
82847
+ exported: true,
82848
+ as: received
82849
+ },
82850
+ [DeviceType.Dishwasher]: {
82851
+ exported: true,
82852
+ as: received
82853
+ }
82854
+ };
82855
+ }
82856
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
82258
82857
  Object.freeze({
82259
82858
  "accessories.getStatus": [{
82260
82859
  name: "deviceId",
@@ -83221,6 +83820,16 @@ Object.freeze({
83221
83820
  form: "single",
83222
83821
  optional: false
83223
83822
  }],
83823
+ "groupActive.getStatus": [{
83824
+ name: "deviceId",
83825
+ form: "single",
83826
+ optional: false
83827
+ }],
83828
+ "groupCommands.getStatus": [{
83829
+ name: "deviceId",
83830
+ form: "single",
83831
+ optional: false
83832
+ }],
83224
83833
  "groupStatus.getStatus": [{
83225
83834
  name: "deviceId",
83226
83835
  form: "single",
@@ -83426,6 +84035,11 @@ Object.freeze({
83426
84035
  form: "single",
83427
84036
  optional: false
83428
84037
  }],
84038
+ "meter.getStatus": [{
84039
+ name: "deviceId",
84040
+ form: "single",
84041
+ optional: false
84042
+ }],
83429
84043
  "motion.getStatus": [{
83430
84044
  name: "deviceId",
83431
84045
  form: "single",
@@ -85130,7 +85744,6 @@ var BucketAccumulator = class {
85130
85744
  return n;
85131
85745
  }
85132
85746
  };
85133
- DeviceType.Camera;
85134
85747
  /**
85135
85748
  * A saved Customize assignment (D936). The schema is the type: callers use
85136
85749
  * `z.infer`, they do not restate the shape.
@@ -85307,6 +85920,8 @@ new Map(Object.entries({
85307
85920
  "detection-blind": "device-events",
85308
85921
  "motion-noisy": "device-events",
85309
85922
  "motion-calm": "device-events",
85923
+ "package-llm-silent": "device-events",
85924
+ "package-llm-answered": "device-events",
85310
85925
  "recording-stalled": "device-events",
85311
85926
  "recording-healthy": "device-events",
85312
85927
  "recording-degraded": "device-events",
@@ -85390,6 +86005,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
85390
86005
  label: "Motion without a subject",
85391
86006
  kinds: ["motion-noisy", "motion-calm"]
85392
86007
  },
86008
+ {
86009
+ id: "package-llm",
86010
+ family: "device-events",
86011
+ label: "Package confirm",
86012
+ kinds: ["package-llm-silent", "package-llm-answered"]
86013
+ },
85393
86014
  {
85394
86015
  id: "recording",
85395
86016
  family: "device-events",
@@ -85571,6 +86192,8 @@ var DEVICE_EVENTS = [
85571
86192
  "detection-blind",
85572
86193
  "motion-noisy",
85573
86194
  "motion-calm",
86195
+ "package-llm-silent",
86196
+ "package-llm-answered",
85574
86197
  "recording-stalled",
85575
86198
  "recording-healthy",
85576
86199
  "recording-degraded",
@@ -85839,6 +86462,15 @@ new Map([
85839
86462
  snapshotLabel: "Cameras with constant motion now",
85840
86463
  snapshotSection: null
85841
86464
  },
86465
+ {
86466
+ id: "package-llm-confirm",
86467
+ family: "device-events",
86468
+ label: "Package confirm (not answering / answering again)",
86469
+ bad: ["package-llm-silent"],
86470
+ recovery: ["package-llm-answered"],
86471
+ snapshotLabel: "Cameras whose package confirm is not answering",
86472
+ snapshotSection: null
86473
+ },
85842
86474
  {
85843
86475
  id: "recording-health",
85844
86476
  family: "device-events",