@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.js CHANGED
@@ -30,7 +30,7 @@ node_fs = __toESM(node_fs);
30
30
  let node_path = require("node:path");
31
31
  node_path = __toESM(node_path);
32
32
  let node_url = require("node:url");
33
- //#region ../types/dist/event-category-DHXxB8t9.mjs
33
+ //#region ../types/dist/event-category-CT-hz5Rb.mjs
34
34
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
35
35
  EventCategory["SystemBoot"] = "system.boot";
36
36
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -356,6 +356,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
356
356
  /** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
357
357
  EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
358
358
  /**
359
+ * A camera's package LLM confirm has failed closed three times in a row
360
+ * (D931, 2026-10-26). One edge per camera until
361
+ * {@link PipelinePackageLlmAnswered}.
362
+ */
363
+ EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
364
+ /** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
365
+ EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
366
+ /**
359
367
  * Per-camera pipeline config was mutated by the orchestrator
360
368
  * (a settings change via `setAgentInferenceDevices` /
361
369
  * `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
@@ -5598,7 +5606,7 @@ var ZodIssueCode = {
5598
5606
  var ZodFirstPartyTypeKind;
5599
5607
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5600
5608
  //#endregion
5601
- //#region ../types/dist/sleep-B81gwQuR.mjs
5609
+ //#region ../types/dist/sleep--jXS9hWo.mjs
5602
5610
  /**
5603
5611
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5604
5612
  * window to float samples (D455).
@@ -9505,6 +9513,8 @@ var CAP_NAMES = Object.freeze([
9505
9513
  "filesystem-browse",
9506
9514
  "flood",
9507
9515
  "gas",
9516
+ "group-active",
9517
+ "group-commands",
9508
9518
  "group-status",
9509
9519
  "heating-valve",
9510
9520
  "humidifier",
@@ -9526,6 +9536,7 @@ var CAP_NAMES = Object.freeze([
9526
9536
  "login-method",
9527
9537
  "media-player",
9528
9538
  "mesh-network",
9539
+ "meter",
9529
9540
  "metrics-provider",
9530
9541
  "model-convert",
9531
9542
  "model-distributor",
@@ -10324,6 +10335,10 @@ Object.freeze({
10324
10335
  "path": "detected",
10325
10336
  "valueType": "boolean"
10326
10337
  }],
10338
+ "group-active": [{
10339
+ "path": "active",
10340
+ "valueType": "boolean"
10341
+ }],
10327
10342
  "group-status": [
10328
10343
  {
10329
10344
  "path": "included",
@@ -10583,6 +10598,10 @@ Object.freeze({
10583
10598
  ]
10584
10599
  }
10585
10600
  ],
10601
+ "meter": [{
10602
+ "path": "value",
10603
+ "valueType": "number"
10604
+ }],
10586
10605
  "motion": [
10587
10606
  {
10588
10607
  "path": "detected",
@@ -11350,7 +11369,7 @@ function sleep(ms) {
11350
11369
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11351
11370
  }
11352
11371
  //#endregion
11353
- //#region ../types/dist/err-msg-BXKzRBwg.mjs
11372
+ //#region ../types/dist/err-msg-DIorGYeC.mjs
11354
11373
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
11355
11374
  DeviceType["Camera"] = "camera";
11356
11375
  DeviceType["Hub"] = "hub";
@@ -11444,8 +11463,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
11444
11463
  DeviceType["PetFeeder"] = "pet-feeder";
11445
11464
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
11446
11465
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
11447
- * Sources: native Bosch Home Connect, reusable by similar integrations. */
11466
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
11467
+ * A plug-built dishwasher carries running-state and power instead. */
11448
11468
  DeviceType["Dishwasher"] = "dishwasher";
11469
+ /** Washing machine. A plug-built one is running while its power stays
11470
+ * above a threshold. No program cap — that surface is the dishwasher's. */
11471
+ DeviceType["Washer"] = "washer";
11472
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
11473
+ DeviceType["Dryer"] = "dryer";
11449
11474
  return DeviceType;
11450
11475
  }({});
11451
11476
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -11703,7 +11728,7 @@ function errMsg$2(err) {
11703
11728
  return String(err);
11704
11729
  }
11705
11730
  //#endregion
11706
- //#region ../types/dist/composition-B85l9j1M.mjs
11731
+ //#region ../types/dist/composition-Bb8iwHEJ.mjs
11707
11732
  /**
11708
11733
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11709
11734
  *
@@ -12147,6 +12172,13 @@ var table = {
12147
12172
  apply: () => {
12148
12173
  throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
12149
12174
  }
12175
+ },
12176
+ schmitt: {
12177
+ minArgs: 3,
12178
+ maxArgs: 5,
12179
+ apply: () => {
12180
+ throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
12181
+ }
12150
12182
  }
12151
12183
  };
12152
12184
  Object.freeze(Object.assign(Object.create(null), table));
@@ -12588,6 +12620,99 @@ function validateExpressionSource(src) {
12588
12620
  return null;
12589
12621
  }
12590
12622
  /**
12623
+ * One power reading becomes a washer, dryer or dishwasher (D944 helper).
12624
+ *
12625
+ * Standby of the control board is under 5 W; a motor or a heater is far
12626
+ * above that, so 5 W is the line. A door lock or a short pump spike is not
12627
+ * a cycle, so running starts only after 30 s above the line.
12628
+ *
12629
+ * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
12630
+ * bursts) lasts several minutes. Ending the cycle on the first dip would
12631
+ * report "finished" in the middle of the program, so the stop dwell is
12632
+ * 10 minutes on every type. The number is stored per type so one machine
12633
+ * can move later without moving the others.
12634
+ */
12635
+ var APPLIANCE_TEMPLATE_ID = "appliance";
12636
+ var APPLIANCE_KINDS = [
12637
+ "washer",
12638
+ "dryer",
12639
+ "dishwasher"
12640
+ ];
12641
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
12642
+ /**
12643
+ * Statistics and integrals over a history read (D944).
12644
+ *
12645
+ * Pure: the same points always give the same answer, so a restart recomputes
12646
+ * rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
12647
+ * never the mean of the samples. A hole with no held value is not integrated
12648
+ * as zero. A truncated read is not published.
12649
+ */
12650
+ var STATISTIC_STATS = [
12651
+ "min",
12652
+ "max",
12653
+ "mean",
12654
+ "sum",
12655
+ "count",
12656
+ "change"
12657
+ ];
12658
+ /**
12659
+ * The range a statistics or integral helper asks history for (D944).
12660
+ *
12661
+ * Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
12662
+ * or 25 hours across a DST change. A tier is eligible only when its retention
12663
+ * is strictly longer than the longest that range can be. Forever qualifies.
12664
+ * Equal to a tier's retention is a refusal, not a short answer.
12665
+ */
12666
+ var MINUTE_MS = 6e4;
12667
+ var HOUR_MS = 60 * MINUTE_MS;
12668
+ var DAY_MS = 24 * HOUR_MS;
12669
+ var ROLLING_SPANS = [
12670
+ "1h",
12671
+ "24h",
12672
+ "2d",
12673
+ "7d",
12674
+ "30d",
12675
+ "custom"
12676
+ ];
12677
+ var CALENDAR_PERIODS = [
12678
+ "today",
12679
+ "yesterday",
12680
+ "this-week",
12681
+ "this-month"
12682
+ ];
12683
+ var RESET_PERIODS = [
12684
+ "never",
12685
+ "daily",
12686
+ "weekly",
12687
+ "monthly"
12688
+ ];
12689
+ 5 * MINUTE_MS;
12690
+ 24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
12691
+ 7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
12692
+ var rollingWindowObject = object({
12693
+ kind: literal("rolling"),
12694
+ span: _enum(ROLLING_SPANS),
12695
+ customMinutes: number().int().min(5).optional()
12696
+ }).check((payload) => {
12697
+ if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
12698
+ code: "custom",
12699
+ message: "a custom range needs a duration in minutes",
12700
+ path: ["customMinutes"],
12701
+ input: payload.value
12702
+ });
12703
+ if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
12704
+ code: "custom",
12705
+ message: "only a custom range carries a duration",
12706
+ path: ["customMinutes"],
12707
+ input: payload.value
12708
+ });
12709
+ });
12710
+ var RollingWindowSchema = rollingWindowObject;
12711
+ var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
12712
+ kind: literal("calendar"),
12713
+ period: _enum(CALENDAR_PERIODS)
12714
+ })]);
12715
+ /**
12591
12716
  * A composition source identity: `(addonId, stableId)`, never a numeric device
12592
12717
  * id. Split out so helper schemas can name it without a cycle through
12593
12718
  * `composition.ts` (D659).
@@ -12611,7 +12736,15 @@ var GROUP_TEMPLATE_IDS = [
12611
12736
  "leak",
12612
12737
  "smoke",
12613
12738
  "occupancy",
12614
- "binary"
12739
+ "binary",
12740
+ "switch",
12741
+ "light",
12742
+ "fan",
12743
+ "cover",
12744
+ "valve",
12745
+ "lock",
12746
+ "media-player",
12747
+ "climate"
12615
12748
  ];
12616
12749
  var NUMERIC_AGGREGATES = [
12617
12750
  "mean",
@@ -12621,6 +12754,8 @@ var NUMERIC_AGGREGATES = [
12621
12754
  "sum"
12622
12755
  ];
12623
12756
  var BINARY_AGGREGATES = ["any", "all"];
12757
+ /** How an actuator group decides it is on. Unknown members never count as off. */
12758
+ var ACTIVE_CRITERIA = ["at-least-one", "all"];
12624
12759
  var AGGREGATE_REDUCERS = [
12625
12760
  "mean",
12626
12761
  "median",
@@ -12632,18 +12767,80 @@ var AGGREGATE_REDUCERS = [
12632
12767
  ];
12633
12768
  var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
12634
12769
  var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
12770
+ var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
12635
12771
  /** One member. The field is stored so occupancy can name motion or binary. */
12636
12772
  var HelperMemberSchema = object({
12637
12773
  source: CompositionSourceRefSchema,
12638
12774
  cap: string().min(1),
12639
12775
  fieldPath: string().min(1)
12640
12776
  });
12641
- var HelperSpecSchema = discriminatedUnion("kind", [object({
12642
- kind: literal("group"),
12643
- templateId: GroupTemplateIdSchema,
12644
- aggregate: AggregateReducerSchema,
12645
- members: array(HelperMemberSchema).min(1).max(64)
12646
- })]);
12777
+ var HelperSpecSchema = discriminatedUnion("kind", [
12778
+ object({
12779
+ kind: literal("group"),
12780
+ templateId: GroupTemplateIdSchema,
12781
+ aggregate: AggregateReducerSchema,
12782
+ members: array(HelperMemberSchema).min(1).max(64),
12783
+ /** Actuator groups only. Readings leave this unset. */
12784
+ active: ActiveCriterionSchema.optional()
12785
+ }),
12786
+ object({
12787
+ kind: literal("appliance"),
12788
+ templateId: literal(APPLIANCE_TEMPLATE_ID),
12789
+ appliance: _enum(APPLIANCE_KINDS),
12790
+ thresholdWatts: number().positive(),
12791
+ startDwellMs: number().int().nonnegative(),
12792
+ stopDwellMs: number().int().nonnegative(),
12793
+ member: HelperMemberSchema
12794
+ }),
12795
+ object({
12796
+ kind: literal("statistic"),
12797
+ stat: _enum(STATISTIC_STATS),
12798
+ window: StatisticWindowSchema,
12799
+ source: HelperMemberSchema
12800
+ }),
12801
+ object({
12802
+ kind: literal("integral"),
12803
+ reset: _enum(RESET_PERIODS),
12804
+ output: _enum(["energy", "meter"]),
12805
+ source: HelperMemberSchema
12806
+ }),
12807
+ object({
12808
+ kind: literal("threshold"),
12809
+ source: HelperMemberSchema,
12810
+ high: number(),
12811
+ low: number(),
12812
+ onForMs: number().int().nonnegative(),
12813
+ offForMs: number().int().nonnegative()
12814
+ }),
12815
+ object({
12816
+ kind: literal("arithmetic"),
12817
+ op: _enum([
12818
+ "sum",
12819
+ "difference",
12820
+ "ratio"
12821
+ ]),
12822
+ inputs: array(HelperMemberSchema).min(2).max(64)
12823
+ }),
12824
+ object({
12825
+ kind: literal("expression"),
12826
+ expr: string().min(1),
12827
+ inputs: array(object({
12828
+ name: string().min(1),
12829
+ member: HelperMemberSchema
12830
+ })).min(1).max(64),
12831
+ output: _enum(["number", "boolean"])
12832
+ }),
12833
+ object({
12834
+ kind: literal("derivative"),
12835
+ window: RollingWindowSchema,
12836
+ per: _enum([
12837
+ "second",
12838
+ "minute",
12839
+ "hour"
12840
+ ]),
12841
+ source: HelperMemberSchema
12842
+ })
12843
+ ]);
12647
12844
  /**
12648
12845
  * A COMPOSITION: a device assembled from other devices' state (D659).
12649
12846
  *
@@ -12725,6 +12922,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12725
12922
  "all"
12726
12923
  ]),
12727
12924
  reads: array(CompositionFieldReadSchema).min(1).max(64),
12925
+ /**
12926
+ * Set when the reads are an enum state, not a yes/no: the listed states
12927
+ * count as active (a cover is `open`). A state outside the list is inactive;
12928
+ * a read that is not a state at all is left out. Only `any` / `all` use it.
12929
+ */
12930
+ activeWhen: array(string().min(1)).min(1).optional(),
12728
12931
  /** `value` is the reading; the others are the member counts. */
12729
12932
  publish: _enum([
12730
12933
  "value",
@@ -12738,6 +12941,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
12738
12941
  "omit",
12739
12942
  "offline"
12740
12943
  ])
12944
+ }),
12945
+ object({
12946
+ kind: literal("series"),
12947
+ read: CompositionFieldReadSchema,
12948
+ /** `value` is the statistic or the integral. The others are the gap counts. */
12949
+ publish: _enum([
12950
+ "value",
12951
+ "included",
12952
+ "excluded",
12953
+ "total"
12954
+ ]),
12955
+ /**
12956
+ * What an unanswered `value` does. Counts ignore this. `offline` leaves the
12957
+ * device unseeded until history answers, so a restart cannot publish 0.
12958
+ */
12959
+ empty: _enum([
12960
+ "null",
12961
+ "omit",
12962
+ "offline"
12963
+ ])
12741
12964
  })
12742
12965
  ]);
12743
12966
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
@@ -18246,7 +18469,22 @@ var BrokerProviderInfoSchema = object({
18246
18469
  */
18247
18470
  interactiveConnectPath: string().min(1).optional(),
18248
18471
  /** Relative hub path to disconnect the linked account (same popup pattern). */
18249
- interactiveDisconnectPath: string().min(1).optional()
18472
+ interactiveDisconnectPath: string().min(1).optional(),
18473
+ /**
18474
+ * The kind signs in through a server-held interactive session instead of
18475
+ * (or before) typed credentials. `basePath` is a hub-relative route set
18476
+ * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
18477
+ * contract: `POST {basePath}/start` (body: the form's settings) answers
18478
+ * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
18479
+ * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
18480
+ * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
18481
+ * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
18482
+ * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
18483
+ * (`{ sessionId }`) drops the session. The dialog that edits a broker
18484
+ * offers it as the default path; the fields the session fills are
18485
+ * marked `filledBySignIn` in the kind's settings schema.
18486
+ */
18487
+ signIn: object({ basePath: string().min(1) }).optional()
18250
18488
  }))
18251
18489
  });
18252
18490
  var ListInputSchema = object({
@@ -19283,7 +19521,8 @@ var CompositionProblemSchema = object({
19283
19521
  "native-capability-replace",
19284
19522
  "mixed-quantity",
19285
19523
  "helper-mismatch",
19286
- "member-constraint"
19524
+ "member-constraint",
19525
+ "retention"
19287
19526
  ]),
19288
19527
  path: string(),
19289
19528
  message: string()
@@ -19517,13 +19756,17 @@ Object.freeze({
19517
19756
  latchedAt: {
19518
19757
  kind: "fixed",
19519
19758
  result: ["number", "null"]
19759
+ },
19760
+ schmitt: {
19761
+ kind: "fixed",
19762
+ result: ["boolean", "null"]
19520
19763
  }
19521
19764
  });
19522
19765
  /**
19523
19766
  * One catalog row per base reading a group helper can build (D944 §3).
19524
19767
  * Data, not a form default written into the block.
19525
19768
  */
19526
- var NOW = {
19769
+ var NOW$2 = {
19527
19770
  fieldPath: "lastFetchedAt",
19528
19771
  expr: "now"
19529
19772
  };
@@ -19537,6 +19780,9 @@ function numeric(input) {
19537
19780
  family: "numeric",
19538
19781
  deviceType: DeviceType.Sensor,
19539
19782
  memberDeviceType: DeviceType.Sensor,
19783
+ memberDeviceTypes: [DeviceType.Sensor],
19784
+ activeWhen: void 0,
19785
+ commandCaps: [],
19540
19786
  baseUnit: baseUnitOf(input.quantity),
19541
19787
  aggregates: NUMERIC_AGGREGATES,
19542
19788
  accept: [{
@@ -19551,6 +19797,9 @@ function binary(input) {
19551
19797
  family: "binary",
19552
19798
  deviceType: DeviceType.Sensor,
19553
19799
  memberDeviceType: DeviceType.Sensor,
19800
+ memberDeviceTypes: [DeviceType.Sensor],
19801
+ activeWhen: void 0,
19802
+ commandCaps: [],
19554
19803
  quantity: "none",
19555
19804
  baseUnit: void 0,
19556
19805
  aggregates: BINARY_AGGREGATES,
@@ -19567,7 +19816,7 @@ var GROUP_TEMPLATES = [
19567
19816
  quantity: "temperature",
19568
19817
  defaultAggregate: "mean",
19569
19818
  empty: "null",
19570
- companions: [NOW]
19819
+ companions: [NOW$2]
19571
19820
  }),
19572
19821
  numeric({
19573
19822
  id: "humidity",
@@ -19578,7 +19827,7 @@ var GROUP_TEMPLATES = [
19578
19827
  quantity: "humidity",
19579
19828
  defaultAggregate: "mean",
19580
19829
  empty: "null",
19581
- companions: [NOW]
19830
+ companions: [NOW$2]
19582
19831
  }),
19583
19832
  numeric({
19584
19833
  id: "pressure",
@@ -19589,7 +19838,7 @@ var GROUP_TEMPLATES = [
19589
19838
  quantity: "pressure",
19590
19839
  defaultAggregate: "mean",
19591
19840
  empty: "null",
19592
- companions: [NOW]
19841
+ companions: [NOW$2]
19593
19842
  }),
19594
19843
  numeric({
19595
19844
  id: "illuminance",
@@ -19600,7 +19849,7 @@ var GROUP_TEMPLATES = [
19600
19849
  quantity: "illuminance",
19601
19850
  defaultAggregate: "mean",
19602
19851
  empty: "null",
19603
- companions: [NOW]
19852
+ companions: [NOW$2]
19604
19853
  }),
19605
19854
  numeric({
19606
19855
  id: "power",
@@ -19611,7 +19860,7 @@ var GROUP_TEMPLATES = [
19611
19860
  quantity: "power",
19612
19861
  defaultAggregate: "sum",
19613
19862
  empty: "omit",
19614
- companions: [NOW]
19863
+ companions: [NOW$2]
19615
19864
  }),
19616
19865
  numeric({
19617
19866
  id: "energy",
@@ -19622,7 +19871,7 @@ var GROUP_TEMPLATES = [
19622
19871
  quantity: "energy",
19623
19872
  defaultAggregate: "sum",
19624
19873
  empty: "omit",
19625
- companions: [NOW]
19874
+ companions: [NOW$2]
19626
19875
  }),
19627
19876
  numeric({
19628
19877
  id: "battery",
@@ -19657,7 +19906,7 @@ var GROUP_TEMPLATES = [
19657
19906
  quantity: "device",
19658
19907
  defaultAggregate: "mean",
19659
19908
  empty: "null",
19660
- companions: [NOW]
19909
+ companions: [NOW$2]
19661
19910
  }),
19662
19911
  binary({
19663
19912
  id: "motion",
@@ -19745,9 +19994,154 @@ var GROUP_TEMPLATES = [
19745
19994
  fieldPath: "on"
19746
19995
  }],
19747
19996
  companions: [CHANGED]
19997
+ }),
19998
+ actuator({
19999
+ id: "switch",
20000
+ summary: "On when at least one switch is on, or when all of them are. Offline and unknown members are left out of that decision, never read as off, and still receive the command.",
20001
+ deviceType: DeviceType.Switch,
20002
+ outputCap: "switch",
20003
+ outputField: "on",
20004
+ member: {
20005
+ cap: "switch",
20006
+ fieldPath: "on"
20007
+ },
20008
+ commandCaps: ["switch"]
20009
+ }),
20010
+ actuator({
20011
+ id: "light",
20012
+ summary: "On when at least one light is on, or when all of them are. Brightness and colour are offered only when every member has them. A command is not sent to a member that lacks it.",
20013
+ deviceType: DeviceType.Light,
20014
+ outputCap: "switch",
20015
+ outputField: "on",
20016
+ member: {
20017
+ cap: "switch",
20018
+ fieldPath: "on"
20019
+ },
20020
+ commandCaps: [
20021
+ "switch",
20022
+ "brightness",
20023
+ "color"
20024
+ ]
20025
+ }),
20026
+ actuator({
20027
+ id: "fan",
20028
+ summary: "On when at least one fan is on, or when all of them are. Speed, preset, direction and oscillation are offered only when every fan has them. A command is not sent to a fan that lacks it.",
20029
+ deviceType: DeviceType.Fan,
20030
+ outputCap: "switch",
20031
+ outputField: "on",
20032
+ member: {
20033
+ cap: "switch",
20034
+ fieldPath: "on"
20035
+ },
20036
+ commandCaps: ["switch", "fan-control"]
20037
+ }),
20038
+ actuator({
20039
+ id: "cover",
20040
+ summary: "Open when at least one cover is open or part-open, or only when all of them are. Position and tilt are offered only when every cover has them. A cover that reports nothing is left out, never read as closed.",
20041
+ deviceType: DeviceType.Cover,
20042
+ outputCap: "group-active",
20043
+ outputField: "active",
20044
+ member: {
20045
+ cap: "cover",
20046
+ fieldPath: "state"
20047
+ },
20048
+ activeWhen: [
20049
+ "open",
20050
+ "opening",
20051
+ "stopped"
20052
+ ],
20053
+ commandCaps: ["cover"]
20054
+ }),
20055
+ actuator({
20056
+ id: "valve",
20057
+ summary: "Open when at least one valve is open or part-open, or only when all of them are. Position is offered only when every valve has it. A valve that reports nothing is left out, never read as closed.",
20058
+ deviceType: DeviceType.Valve,
20059
+ outputCap: "group-active",
20060
+ outputField: "active",
20061
+ member: {
20062
+ cap: "valve",
20063
+ fieldPath: "state"
20064
+ },
20065
+ activeWhen: [
20066
+ "open",
20067
+ "opening",
20068
+ "stopped"
20069
+ ],
20070
+ commandCaps: ["valve"]
20071
+ }),
20072
+ actuator({
20073
+ id: "lock",
20074
+ summary: "Locked when at least one lock is locked, or only when all of them are. A jammed or moving lock is not locked. Locking is one tap; unlocking the group needs a confirmation every time, and nothing is sent without it.",
20075
+ deviceType: DeviceType.Lock,
20076
+ outputCap: "group-active",
20077
+ outputField: "active",
20078
+ member: {
20079
+ cap: "lock-control",
20080
+ fieldPath: "state"
20081
+ },
20082
+ activeWhen: ["locked"],
20083
+ commandCaps: ["lock-control"]
20084
+ }),
20085
+ actuator({
20086
+ id: "media-player",
20087
+ summary: "Playing when at least one player is playing, or only when all of them are. Volume is offered only when every player has it. A player that reports nothing is left out, never read as stopped.",
20088
+ deviceType: DeviceType.MediaPlayer,
20089
+ outputCap: "group-active",
20090
+ outputField: "active",
20091
+ member: {
20092
+ cap: "media-player",
20093
+ fieldPath: "state"
20094
+ },
20095
+ activeWhen: ["playing", "buffering"],
20096
+ commandCaps: ["media-player"]
20097
+ }),
20098
+ actuator({
20099
+ id: "climate",
20100
+ summary: "Running when at least one thermostat or air conditioner is not off, or only when all of them are. Target temperature and fan are offered only when every device has them. A device that reports nothing is left out, never read as off.",
20101
+ deviceType: DeviceType.Thermostat,
20102
+ memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
20103
+ outputCap: "group-active",
20104
+ outputField: "active",
20105
+ member: {
20106
+ cap: "climate-control",
20107
+ fieldPath: "mode"
20108
+ },
20109
+ activeWhen: [
20110
+ "heat",
20111
+ "cool",
20112
+ "auto",
20113
+ "heat_cool",
20114
+ "fan_only",
20115
+ "dry"
20116
+ ],
20117
+ commandCaps: ["climate-control"]
19748
20118
  })
19749
20119
  ];
20120
+ function actuator(input) {
20121
+ return {
20122
+ id: input.id,
20123
+ summary: input.summary,
20124
+ deviceType: input.deviceType,
20125
+ outputCap: input.outputCap,
20126
+ outputField: input.outputField,
20127
+ commandCaps: input.commandCaps,
20128
+ family: "actuator",
20129
+ role: void 0,
20130
+ memberDeviceType: input.deviceType,
20131
+ memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
20132
+ activeWhen: input.activeWhen,
20133
+ quantity: "none",
20134
+ baseUnit: void 0,
20135
+ aggregates: BINARY_AGGREGATES,
20136
+ defaultAggregate: "any",
20137
+ empty: "offline",
20138
+ accept: [input.member],
20139
+ companions: [CHANGED]
20140
+ };
20141
+ }
19750
20142
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
20143
+ DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
20144
+ DeviceType.Camera;
19751
20145
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19752
20146
  /**
19753
20147
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -44762,7 +45156,7 @@ var NetworkAccessStatusSchema = object({
44762
45156
  });
44763
45157
  /**
44764
45158
  * Optional, richer endpoint shape returned by providers that expose
44765
- * MORE than one ingress concurrently (Tailscale Ingress with mixed
45159
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44766
45160
  * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44767
45161
  * the originating provider config (mode + sourcePort) so the
44768
45162
  * orchestrator UI can label rows distinctly. Providers that expose only
@@ -46582,6 +46976,8 @@ var NcSystemEventKindSchema = _enum([
46582
46976
  "detection-blind",
46583
46977
  "motion-noisy",
46584
46978
  "motion-calm",
46979
+ "package-llm-silent",
46980
+ "package-llm-answered",
46585
46981
  "recording-stalled",
46586
46982
  "recording-healthy",
46587
46983
  "recording-degraded",
@@ -47671,7 +48067,20 @@ var NcRuleInputSchema = object({
47671
48067
  * save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
47672
48068
  * path does not run Zod defaults.
47673
48069
  */
47674
- groupingSec: number().int().min(5).max(60).optional()
48070
+ groupingSec: number().int().min(5).max(60).optional(),
48071
+ /**
48072
+ * ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
48073
+ * carries "Correct" / "Wrong" buttons (and, where the channel has room, a
48074
+ * "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
48075
+ * same mark the Library writes, and records whose notification it was and
48076
+ * when. A channel with no buttons gets the same two choices as links.
48077
+ *
48078
+ * `.optional()`, never `.default()` (a Zod default does not run on the addon
48079
+ * cap path): absent and `false` both mean off, which is what every rule
48080
+ * authored before this field meant. An editor turns it off by writing
48081
+ * `false`, so it needs no `clear` key.
48082
+ */
48083
+ askVerdict: boolean().optional()
47675
48084
  });
47676
48085
  /**
47677
48086
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -58519,6 +58928,28 @@ var PackageVersionInfoSchema = object({
58519
58928
  deprecated: string().optional(),
58520
58929
  distTags: array(string()).readonly()
58521
58930
  });
58931
+ /**
58932
+ * One published document. `ok` is markdown from the registry. `missing` means
58933
+ * the registry answered and the file was not there. `error` is a registry or
58934
+ * tarball failure — never an empty string standing in for either.
58935
+ */
58936
+ var PackageDocBodySchema = discriminatedUnion("status", [
58937
+ object({
58938
+ status: literal("ok"),
58939
+ markdown: string()
58940
+ }),
58941
+ object({ status: literal("missing") }),
58942
+ object({
58943
+ status: literal("error"),
58944
+ message: string()
58945
+ })
58946
+ ]);
58947
+ var PackageDocsSchema = object({
58948
+ name: string(),
58949
+ version: string(),
58950
+ readme: PackageDocBodySchema,
58951
+ changelog: PackageDocBodySchema
58952
+ });
58522
58953
  var SearchResultSchema = object({
58523
58954
  name: string(),
58524
58955
  version: string(),
@@ -58737,7 +59168,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
58737
59168
  }), object({ success: literal(true) }), {
58738
59169
  kind: "mutation",
58739
59170
  auth: "admin"
58740
- }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
59171
+ }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
59172
+ name: string().min(1),
59173
+ version: string().min(1).optional()
59174
+ }), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
58741
59175
  kind: "mutation",
58742
59176
  auth: "admin"
58743
59177
  }), method(object({ packageName: string() }), object({ success: literal(true) }), {
@@ -59026,7 +59460,7 @@ var BatteryStatusSchema = object({
59026
59460
  */
59027
59461
  binary: boolean().optional().meta({ label: "Binary indicator" })
59028
59462
  });
59029
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat;
59463
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter;
59030
59464
  method(object({
59031
59465
  deviceId: number(),
59032
59466
  /** Bound on the wait. Sensible range 3000–10000ms. */
@@ -60768,6 +61202,24 @@ object({
60768
61202
  lastChangedAt: number()
60769
61203
  });
60770
61204
  DeviceType.Sensor;
61205
+ object({
61206
+ /** One row per member that has been commanded. A later command replaces that member's row. */
61207
+ results: array(object({
61208
+ deviceId: number().int().nonnegative(),
61209
+ cap: string().min(1),
61210
+ method: string().min(1),
61211
+ ok: boolean(),
61212
+ error: string().optional(),
61213
+ /** Ms epoch of the attempt. */
61214
+ at: number()
61215
+ })) });
61216
+ DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
61217
+ object({
61218
+ active: boolean().meta({ label: "Active" }),
61219
+ /** Ms epoch when the slice was last updated. */
61220
+ lastChangedAt: number()
61221
+ });
61222
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
60771
61223
  object({
60772
61224
  /** Members whose current value entered the aggregate. */
60773
61225
  included: number().int().nonnegative().meta(readingMeta("count", {
@@ -60785,7 +61237,7 @@ object({
60785
61237
  label: "Members"
60786
61238
  }))
60787
61239
  });
60788
- DeviceType.Sensor;
61240
+ DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
60789
61241
  object({
60790
61242
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
60791
61243
  position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -61692,7 +62144,14 @@ DeviceType.Lock, method(object({
61692
62144
  auth: "protected"
61693
62145
  }), method(object({
61694
62146
  deviceId: number().int().nonnegative(),
61695
- code: string().min(1).optional()
62147
+ code: string().min(1).optional(),
62148
+ /**
62149
+ * Honoured by a helper lock GROUP only (D944): unlocking several doors
62150
+ * with one call is refused unless the caller says it was confirmed. A
62151
+ * single lock ignores it. The refusal lives in the callee, so no
62152
+ * automation or API caller reaches the doors by skipping a dialog.
62153
+ */
62154
+ confirm: boolean().optional()
61696
62155
  }), _void(), {
61697
62156
  kind: "mutation",
61698
62157
  auth: "protected"
@@ -61700,6 +62159,11 @@ DeviceType.Lock, method(object({
61700
62159
  kind: "mutation",
61701
62160
  auth: "protected"
61702
62161
  });
62162
+ object({
62163
+ value: number().nullable().meta(readingMeta("device", { label: "Total" })),
62164
+ lastFetchedAt: number()
62165
+ });
62166
+ DeviceType.Sensor;
61703
62167
  /**
61704
62168
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
61705
62169
  * Apple TV, Spotify, Roku, generic OTT receivers, …) on
@@ -64348,9 +64812,9 @@ object({
64348
64812
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64349
64813
  kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64350
64814
  /** Voltage in volts. */
64351
- volts: number().optional().meta(readingMeta("voltage")),
64815
+ volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64352
64816
  /** Current in amperes. */
64353
- amps: number().optional().meta(readingMeta("current")),
64817
+ amps: number().optional().meta(readingMeta("current", { label: "Current" })),
64354
64818
  /** Apparent power in volt-amperes. */
64355
64819
  va: number().optional().meta(readingMeta("apparent-power")),
64356
64820
  /** Power factor (0..1) if reported. */
@@ -64358,7 +64822,23 @@ object({
64358
64822
  /** Ms epoch when the slice was last updated. */
64359
64823
  lastFetchedAt: number()
64360
64824
  });
64361
- DeviceType.Sensor;
64825
+ [
64826
+ DeviceType.Sensor,
64827
+ DeviceType.Switch,
64828
+ DeviceType.Light,
64829
+ DeviceType.Fan,
64830
+ DeviceType.Climate,
64831
+ DeviceType.Thermostat,
64832
+ DeviceType.Humidifier,
64833
+ DeviceType.WaterHeater,
64834
+ DeviceType.MediaPlayer,
64835
+ DeviceType.Washer,
64836
+ DeviceType.Dryer,
64837
+ DeviceType.Dishwasher,
64838
+ DeviceType.Vacuum,
64839
+ DeviceType.LawnMower,
64840
+ DeviceType.PetFeeder
64841
+ ].filter((type) => type !== DeviceType.Sensor);
64362
64842
  /**
64363
64843
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
64364
64844
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -64805,7 +65285,7 @@ object({
64805
65285
  phase: string().nullable()
64806
65286
  });
64807
65287
  Object.values(DeviceType);
64808
- DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
65288
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
64809
65289
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
64810
65290
  kind: "mutation",
64811
65291
  auth: "admin"
@@ -66878,7 +67358,7 @@ object({
66878
67358
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
66879
67359
  lastChangedAt: number()
66880
67360
  });
66881
- DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
67361
+ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
66882
67362
  deviceId: number(),
66883
67363
  on: boolean()
66884
67364
  }), _void(), {
@@ -68630,8 +69110,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
68630
69110
  kind: "mutation",
68631
69111
  auth: "admin"
68632
69112
  });
69113
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
68633
69114
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
68634
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Camera, DeviceType.Camera, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
69115
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
68635
69116
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
68636
69117
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
68637
69118
  new Set(CAP_NAMES);
@@ -68684,7 +69165,7 @@ var AccessoryKind = {
68684
69165
  PrivacyMask: DeviceRole.PrivacyMask
68685
69166
  };
68686
69167
  AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
68687
- DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
69168
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
68688
69169
  new Set(Object.values(DeviceType));
68689
69170
  Object.freeze({
68690
69171
  "accessories.getStatus": {
@@ -68792,6 +69273,13 @@ Object.freeze({
68792
69273
  access: "view",
68793
69274
  role: "admin"
68794
69275
  },
69276
+ "addons.getPackageDocs": {
69277
+ capName: "addons",
69278
+ capScope: "system",
69279
+ addonId: null,
69280
+ access: "view",
69281
+ role: "admin"
69282
+ },
68795
69283
  "addons.getVersions": {
68796
69284
  capName: "addons",
68797
69285
  capScope: "system",
@@ -71900,6 +72388,20 @@ Object.freeze({
71900
72388
  access: "view",
71901
72389
  role: "viewer"
71902
72390
  },
72391
+ "groupActive.getStatus": {
72392
+ capName: "group-active",
72393
+ capScope: "device",
72394
+ addonId: null,
72395
+ access: "view",
72396
+ role: "viewer"
72397
+ },
72398
+ "groupCommands.getStatus": {
72399
+ capName: "group-commands",
72400
+ capScope: "device",
72401
+ addonId: null,
72402
+ access: "view",
72403
+ role: "viewer"
72404
+ },
71903
72405
  "groupStatus.getStatus": {
71904
72406
  capName: "group-status",
71905
72407
  capScope: "device",
@@ -72859,6 +73361,13 @@ Object.freeze({
72859
73361
  access: "create",
72860
73362
  role: "admin"
72861
73363
  },
73364
+ "meter.getStatus": {
73365
+ capName: "meter",
73366
+ capScope: "device",
73367
+ addonId: null,
73368
+ access: "view",
73369
+ role: "viewer"
73370
+ },
72862
73371
  "metricsProvider.collectSnapshot": {
72863
73372
  capName: "metrics-provider",
72864
73373
  capScope: "system",
@@ -80957,7 +81466,39 @@ var APPLIANCE_CAP_VOCABULARY = {
80957
81466
  phrase: t("When {device} finishes", "Quando {device} finisce"),
80958
81467
  icon: "square"
80959
81468
  }
80960
- }
81469
+ },
81470
+ overrides: { byType: {
81471
+ [DeviceType.Washer]: {
81472
+ state: { running: {
81473
+ whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
81474
+ whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
81475
+ } },
81476
+ events: {
81477
+ onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
81478
+ onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
81479
+ }
81480
+ },
81481
+ [DeviceType.Dryer]: {
81482
+ state: { running: {
81483
+ whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
81484
+ whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
81485
+ } },
81486
+ events: {
81487
+ onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
81488
+ onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
81489
+ }
81490
+ },
81491
+ [DeviceType.Dishwasher]: {
81492
+ state: { running: {
81493
+ whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
81494
+ whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
81495
+ } },
81496
+ events: {
81497
+ onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
81498
+ onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
81499
+ }
81500
+ }
81501
+ } }
80961
81502
  },
80962
81503
  update: {
80963
81504
  scope: "device",
@@ -81729,6 +82270,14 @@ var SENSOR_CAP_VOCABULARY = {
81729
82270
  icon: "flame"
81730
82271
  }]
81731
82272
  },
82273
+ "group-active": {
82274
+ scope: "device",
82275
+ noun: t("Group state", "Stato del gruppo")
82276
+ },
82277
+ "group-commands": {
82278
+ scope: "device",
82279
+ noun: t("Group command", "Comando di gruppo")
82280
+ },
81732
82281
  "group-status": {
81733
82282
  scope: "device",
81734
82283
  noun: t("Group", "Gruppo"),
@@ -81781,6 +82330,17 @@ var SENSOR_CAP_VOCABULARY = {
81781
82330
  }],
81782
82331
  events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
81783
82332
  },
82333
+ meter: {
82334
+ scope: "device",
82335
+ noun: t("Meter", "Contatore"),
82336
+ state: [{
82337
+ kind: "numeric",
82338
+ field: "value",
82339
+ quantity: t("the total", "il totale"),
82340
+ label: t("Total", "Totale"),
82341
+ icon: "gauge"
82342
+ }]
82343
+ },
81784
82344
  "numeric-sensor": {
81785
82345
  scope: "device",
81786
82346
  noun: t("Sensor", "Sensore"),
@@ -82253,7 +82813,7 @@ function projectAction(cap, action) {
82253
82813
  };
82254
82814
  }
82255
82815
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
82256
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
82816
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
82257
82817
  /**
82258
82818
  * Heuristic check — `device.features?.includes(profile.when.hasFeature)`.
82259
82819
  */
@@ -82283,6 +82843,45 @@ function resolveDeviceProfile(features) {
82283
82843
  for (const profile of DEVICE_PROFILES) if (deviceMatchesProfile(features, profile)) return profile;
82284
82844
  return null;
82285
82845
  }
82846
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
82847
+ var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
82848
+ var HOMEKIT_REASON = "HomeKit has no laundry-appliance service. The export tab would pair an accessory with nothing on it: running-state is withheld until a null reading can be unavailable, and power is not a HomeKit service this export honours.";
82849
+ var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
82850
+ var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
82851
+ var MATTER_REASON = "No Matter export is built. The closest device types are Laundry Washer (0x0073) and Laundry Dryer (0x0074); a power threshold is not the Laundry Washer Mode cluster.";
82852
+ function withheld(reason) {
82853
+ return {
82854
+ [DeviceType.Washer]: {
82855
+ exported: false,
82856
+ reason
82857
+ },
82858
+ [DeviceType.Dryer]: {
82859
+ exported: false,
82860
+ reason
82861
+ },
82862
+ [DeviceType.Dishwasher]: {
82863
+ exported: false,
82864
+ reason
82865
+ }
82866
+ };
82867
+ }
82868
+ function offered(received) {
82869
+ return {
82870
+ [DeviceType.Washer]: {
82871
+ exported: true,
82872
+ as: received
82873
+ },
82874
+ [DeviceType.Dryer]: {
82875
+ exported: true,
82876
+ as: received
82877
+ },
82878
+ [DeviceType.Dishwasher]: {
82879
+ exported: true,
82880
+ as: received
82881
+ }
82882
+ };
82883
+ }
82884
+ offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
82286
82885
  Object.freeze({
82287
82886
  "accessories.getStatus": [{
82288
82887
  name: "deviceId",
@@ -83249,6 +83848,16 @@ Object.freeze({
83249
83848
  form: "single",
83250
83849
  optional: false
83251
83850
  }],
83851
+ "groupActive.getStatus": [{
83852
+ name: "deviceId",
83853
+ form: "single",
83854
+ optional: false
83855
+ }],
83856
+ "groupCommands.getStatus": [{
83857
+ name: "deviceId",
83858
+ form: "single",
83859
+ optional: false
83860
+ }],
83252
83861
  "groupStatus.getStatus": [{
83253
83862
  name: "deviceId",
83254
83863
  form: "single",
@@ -83454,6 +84063,11 @@ Object.freeze({
83454
84063
  form: "single",
83455
84064
  optional: false
83456
84065
  }],
84066
+ "meter.getStatus": [{
84067
+ name: "deviceId",
84068
+ form: "single",
84069
+ optional: false
84070
+ }],
83457
84071
  "motion.getStatus": [{
83458
84072
  name: "deviceId",
83459
84073
  form: "single",
@@ -85158,7 +85772,6 @@ var BucketAccumulator = class {
85158
85772
  return n;
85159
85773
  }
85160
85774
  };
85161
- DeviceType.Camera;
85162
85775
  /**
85163
85776
  * A saved Customize assignment (D936). The schema is the type: callers use
85164
85777
  * `z.infer`, they do not restate the shape.
@@ -85335,6 +85948,8 @@ new Map(Object.entries({
85335
85948
  "detection-blind": "device-events",
85336
85949
  "motion-noisy": "device-events",
85337
85950
  "motion-calm": "device-events",
85951
+ "package-llm-silent": "device-events",
85952
+ "package-llm-answered": "device-events",
85338
85953
  "recording-stalled": "device-events",
85339
85954
  "recording-healthy": "device-events",
85340
85955
  "recording-degraded": "device-events",
@@ -85418,6 +86033,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
85418
86033
  label: "Motion without a subject",
85419
86034
  kinds: ["motion-noisy", "motion-calm"]
85420
86035
  },
86036
+ {
86037
+ id: "package-llm",
86038
+ family: "device-events",
86039
+ label: "Package confirm",
86040
+ kinds: ["package-llm-silent", "package-llm-answered"]
86041
+ },
85421
86042
  {
85422
86043
  id: "recording",
85423
86044
  family: "device-events",
@@ -85599,6 +86220,8 @@ var DEVICE_EVENTS = [
85599
86220
  "detection-blind",
85600
86221
  "motion-noisy",
85601
86222
  "motion-calm",
86223
+ "package-llm-silent",
86224
+ "package-llm-answered",
85602
86225
  "recording-stalled",
85603
86226
  "recording-healthy",
85604
86227
  "recording-degraded",
@@ -85867,6 +86490,15 @@ new Map([
85867
86490
  snapshotLabel: "Cameras with constant motion now",
85868
86491
  snapshotSection: null
85869
86492
  },
86493
+ {
86494
+ id: "package-llm-confirm",
86495
+ family: "device-events",
86496
+ label: "Package confirm (not answering / answering again)",
86497
+ bad: ["package-llm-silent"],
86498
+ recovery: ["package-llm-answered"],
86499
+ snapshotLabel: "Cameras whose package confirm is not answering",
86500
+ snapshotSection: null
86501
+ },
85870
86502
  {
85871
86503
  id: "recording-health",
85872
86504
  family: "device-events",