@camstack/addon-provider-ecowitt 0.2.147 → 0.2.148

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createServer } from "http";
2
2
  import { EventEmitter } from "events";
3
3
  import { createSocket } from "dgram";
4
- //#region ../types/dist/event-category-DHXxB8t9.mjs
4
+ //#region ../types/dist/event-category-CT-hz5Rb.mjs
5
5
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
6
6
  EventCategory["SystemBoot"] = "system.boot";
7
7
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -327,6 +327,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
327
327
  /** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
328
328
  EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
329
329
  /**
330
+ * A camera's package LLM confirm has failed closed three times in a row
331
+ * (D931, 2026-10-26). One edge per camera until
332
+ * {@link PipelinePackageLlmAnswered}.
333
+ */
334
+ EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
335
+ /** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
336
+ EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
337
+ /**
330
338
  * Per-camera pipeline config was mutated by the orchestrator
331
339
  * (a settings change via `setAgentInferenceDevices` /
332
340
  * `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
@@ -5436,7 +5444,7 @@ var ZodIssueCode$1 = {
5436
5444
  var ZodFirstPartyTypeKind$1;
5437
5445
  ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {});
5438
5446
  //#endregion
5439
- //#region ../types/dist/sleep-CARd6wZF.mjs
5447
+ //#region ../types/dist/sleep-bykKrTgu.mjs
5440
5448
  /**
5441
5449
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5442
5450
  * window to float samples (D455).
@@ -8629,6 +8637,8 @@ var CAP_NAMES = Object.freeze([
8629
8637
  "filesystem-browse",
8630
8638
  "flood",
8631
8639
  "gas",
8640
+ "group-commands",
8641
+ "group-status",
8632
8642
  "heating-valve",
8633
8643
  "humidifier",
8634
8644
  "humidity-sensor",
@@ -9447,6 +9457,20 @@ Object.freeze({
9447
9457
  "path": "detected",
9448
9458
  "valueType": "boolean"
9449
9459
  }],
9460
+ "group-status": [
9461
+ {
9462
+ "path": "included",
9463
+ "valueType": "number"
9464
+ },
9465
+ {
9466
+ "path": "excluded",
9467
+ "valueType": "number"
9468
+ },
9469
+ {
9470
+ "path": "total",
9471
+ "valueType": "number"
9472
+ }
9473
+ ],
9450
9474
  "heating-valve": [{
9451
9475
  "path": "position",
9452
9476
  "valueType": "number"
@@ -10802,7 +10826,7 @@ function errMsg(err) {
10802
10826
  return String(err);
10803
10827
  }
10804
10828
  //#endregion
10805
- //#region ../types/dist/composition-kEl9mevu.mjs
10829
+ //#region ../types/dist/composition-OhpuespA.mjs
10806
10830
  /**
10807
10831
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10808
10832
  *
@@ -11557,15 +11581,74 @@ function validateExpressionSource(src) {
11557
11581
  }
11558
11582
  return null;
11559
11583
  }
11560
- /** A composed device's stableId is this prefix plus the block id. */
11561
- var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11562
- /** Longest snippet a `code` source may carry (slice 4). */
11563
- var MAX_COMPOSITION_CODE_LENGTH = 2e4;
11584
+ /**
11585
+ * A composition source identity: `(addonId, stableId)`, never a numeric device
11586
+ * id. Split out so helper schemas can name it without a cycle through
11587
+ * `composition.ts` (D659).
11588
+ */
11564
11589
  var CompositionSourceRefSchema = object({
11565
11590
  /** The owning addon; `stableId` is unique only within it. */
11566
11591
  addonId: string().min(1),
11567
11592
  stableId: string().min(1)
11568
11593
  });
11594
+ var GROUP_TEMPLATE_IDS = [
11595
+ "temperature",
11596
+ "humidity",
11597
+ "pressure",
11598
+ "illuminance",
11599
+ "power",
11600
+ "energy",
11601
+ "battery",
11602
+ "numeric",
11603
+ "motion",
11604
+ "contact",
11605
+ "leak",
11606
+ "smoke",
11607
+ "occupancy",
11608
+ "binary",
11609
+ "switch",
11610
+ "light"
11611
+ ];
11612
+ var NUMERIC_AGGREGATES = [
11613
+ "mean",
11614
+ "median",
11615
+ "min",
11616
+ "max",
11617
+ "sum"
11618
+ ];
11619
+ var BINARY_AGGREGATES = ["any", "all"];
11620
+ /** How an actuator group decides it is on. Unknown members never count as off. */
11621
+ var ACTIVE_CRITERIA = ["at-least-one", "all"];
11622
+ var AGGREGATE_REDUCERS = [
11623
+ "mean",
11624
+ "median",
11625
+ "min",
11626
+ "max",
11627
+ "sum",
11628
+ "any",
11629
+ "all"
11630
+ ];
11631
+ var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
11632
+ var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
11633
+ var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
11634
+ /** One member. The field is stored so occupancy can name motion or binary. */
11635
+ var HelperMemberSchema = object({
11636
+ source: CompositionSourceRefSchema,
11637
+ cap: string().min(1),
11638
+ fieldPath: string().min(1)
11639
+ });
11640
+ var HelperSpecSchema = discriminatedUnion("kind", [object({
11641
+ kind: literal("group"),
11642
+ templateId: GroupTemplateIdSchema,
11643
+ aggregate: AggregateReducerSchema,
11644
+ members: array(HelperMemberSchema).min(1).max(64),
11645
+ /** Actuator groups only. Readings leave this unset. */
11646
+ active: ActiveCriterionSchema.optional()
11647
+ })]);
11648
+ /** A composed device's stableId is this prefix plus the block id. */
11649
+ var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11650
+ /** Longest snippet a `code` source may carry (slice 4). */
11651
+ var MAX_COMPOSITION_CODE_LENGTH = 2e4;
11569
11652
  /** One field of one capability of one source device. */
11570
11653
  var CompositionFieldReadSchema = object({
11571
11654
  source: CompositionSourceRefSchema,
@@ -11609,7 +11692,33 @@ var CompositionCodeSourceSchema = object({
11609
11692
  var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11610
11693
  CompositionFromSourceSchema,
11611
11694
  CompositionExpressionSourceSchema,
11612
- CompositionCodeSourceSchema
11695
+ CompositionCodeSourceSchema,
11696
+ object({
11697
+ kind: literal("aggregate"),
11698
+ reducer: _enum([
11699
+ "mean",
11700
+ "median",
11701
+ "min",
11702
+ "max",
11703
+ "sum",
11704
+ "any",
11705
+ "all"
11706
+ ]),
11707
+ reads: array(CompositionFieldReadSchema).min(1).max(64),
11708
+ /** `value` is the reading; the others are the member counts. */
11709
+ publish: _enum([
11710
+ "value",
11711
+ "included",
11712
+ "excluded",
11713
+ "total"
11714
+ ]),
11715
+ /** What a `value` publish does when nobody is included. Counts ignore this. */
11716
+ empty: _enum([
11717
+ "null",
11718
+ "omit",
11719
+ "offline"
11720
+ ])
11721
+ })
11613
11722
  ]);
11614
11723
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
11615
11724
  kind: literal("forward"),
@@ -11655,7 +11764,13 @@ var CompositionSchema = object({
11655
11764
  kind: literal("existing"),
11656
11765
  device: CompositionSourceRefSchema
11657
11766
  })]),
11658
- features: array(CompositionFeatureSchema).min(1).max(16)
11767
+ features: array(CompositionFeatureSchema).min(1).max(16),
11768
+ /**
11769
+ * Present when the block is a helper template. Absent on a free
11770
+ * composition (the room example, device 4603). The editor reads this,
11771
+ * never the compiled features.
11772
+ */
11773
+ helper: HelperSpecSchema.optional()
11659
11774
  }).superRefine((composition, ctx) => {
11660
11775
  const seen = /* @__PURE__ */ new Set();
11661
11776
  composition.features.forEach((feature, index) => {
@@ -17864,7 +17979,10 @@ var CompositionProblemSchema = object({
17864
17979
  "target-missing",
17865
17980
  "target-unreadable",
17866
17981
  "not-replaceable",
17867
- "native-capability-replace"
17982
+ "native-capability-replace",
17983
+ "mixed-quantity",
17984
+ "helper-mismatch",
17985
+ "member-constraint"
17868
17986
  ]),
17869
17987
  path: string(),
17870
17988
  message: string()
@@ -17885,7 +18003,8 @@ var CompositionUnavailableModeSchema = _enum([
17885
18003
  "null",
17886
18004
  "offline",
17887
18005
  "unknown",
17888
- "release"
18006
+ "release",
18007
+ "omit"
17889
18008
  ]);
17890
18009
  /**
17891
18010
  * `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
@@ -18099,6 +18218,275 @@ Object.freeze({
18099
18218
  result: ["number", "null"]
18100
18219
  }
18101
18220
  });
18221
+ /**
18222
+ * One catalog row per base reading a group helper can build (D944 §3).
18223
+ * Data, not a form default written into the block.
18224
+ */
18225
+ var NOW = {
18226
+ fieldPath: "lastFetchedAt",
18227
+ expr: "now"
18228
+ };
18229
+ var CHANGED = {
18230
+ fieldPath: "lastChangedAt",
18231
+ expr: "now"
18232
+ };
18233
+ function numeric(input) {
18234
+ return {
18235
+ ...input,
18236
+ family: "numeric",
18237
+ deviceType: DeviceType.Sensor,
18238
+ memberDeviceType: DeviceType.Sensor,
18239
+ commandCaps: [],
18240
+ baseUnit: baseUnitOf(input.quantity),
18241
+ aggregates: NUMERIC_AGGREGATES,
18242
+ accept: [{
18243
+ cap: input.outputCap,
18244
+ fieldPath: input.outputField
18245
+ }]
18246
+ };
18247
+ }
18248
+ function binary(input) {
18249
+ return {
18250
+ ...input,
18251
+ family: "binary",
18252
+ deviceType: DeviceType.Sensor,
18253
+ memberDeviceType: DeviceType.Sensor,
18254
+ commandCaps: [],
18255
+ quantity: "none",
18256
+ baseUnit: void 0,
18257
+ aggregates: BINARY_AGGREGATES,
18258
+ empty: "offline"
18259
+ };
18260
+ }
18261
+ var GROUP_TEMPLATES = [
18262
+ numeric({
18263
+ id: "temperature",
18264
+ summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18265
+ role: DeviceRole.TemperatureSensor,
18266
+ outputCap: "temperature-sensor",
18267
+ outputField: "celsius",
18268
+ quantity: "temperature",
18269
+ defaultAggregate: "mean",
18270
+ empty: "null",
18271
+ companions: [NOW]
18272
+ }),
18273
+ numeric({
18274
+ id: "humidity",
18275
+ summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18276
+ role: DeviceRole.HumiditySensor,
18277
+ outputCap: "humidity-sensor",
18278
+ outputField: "percent",
18279
+ quantity: "humidity",
18280
+ defaultAggregate: "mean",
18281
+ empty: "null",
18282
+ companions: [NOW]
18283
+ }),
18284
+ numeric({
18285
+ id: "pressure",
18286
+ summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18287
+ role: DeviceRole.PressureSensor,
18288
+ outputCap: "pressure-sensor",
18289
+ outputField: "hpa",
18290
+ quantity: "pressure",
18291
+ defaultAggregate: "mean",
18292
+ empty: "null",
18293
+ companions: [NOW]
18294
+ }),
18295
+ numeric({
18296
+ id: "illuminance",
18297
+ summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18298
+ role: DeviceRole.AmbientLightSensor,
18299
+ outputCap: "ambient-light-sensor",
18300
+ outputField: "lux",
18301
+ quantity: "illuminance",
18302
+ defaultAggregate: "mean",
18303
+ empty: "null",
18304
+ companions: [NOW]
18305
+ }),
18306
+ numeric({
18307
+ id: "power",
18308
+ summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18309
+ role: DeviceRole.PowerSensor,
18310
+ outputCap: "power-meter",
18311
+ outputField: "watts",
18312
+ quantity: "power",
18313
+ defaultAggregate: "sum",
18314
+ empty: "omit",
18315
+ companions: [NOW]
18316
+ }),
18317
+ numeric({
18318
+ id: "energy",
18319
+ summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18320
+ role: DeviceRole.EnergySensor,
18321
+ outputCap: "power-meter",
18322
+ outputField: "kwhTotal",
18323
+ quantity: "energy",
18324
+ defaultAggregate: "sum",
18325
+ empty: "omit",
18326
+ companions: [NOW]
18327
+ }),
18328
+ numeric({
18329
+ id: "battery",
18330
+ summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18331
+ role: DeviceRole.BatterySensor,
18332
+ outputCap: "battery",
18333
+ outputField: "percentage",
18334
+ quantity: "percentage",
18335
+ defaultAggregate: "mean",
18336
+ empty: "null",
18337
+ companions: [
18338
+ {
18339
+ fieldPath: "charging",
18340
+ expr: "\"unknown\""
18341
+ },
18342
+ {
18343
+ fieldPath: "sleeping",
18344
+ expr: "false"
18345
+ },
18346
+ {
18347
+ fieldPath: "lastUpdated",
18348
+ expr: "now"
18349
+ }
18350
+ ]
18351
+ }),
18352
+ numeric({
18353
+ id: "numeric",
18354
+ summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
18355
+ role: DeviceRole.NumericSensor,
18356
+ outputCap: "numeric-sensor",
18357
+ outputField: "value",
18358
+ quantity: "device",
18359
+ defaultAggregate: "mean",
18360
+ empty: "null",
18361
+ companions: [NOW]
18362
+ }),
18363
+ binary({
18364
+ id: "motion",
18365
+ summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
18366
+ role: DeviceRole.MotionSensor,
18367
+ outputCap: "motion",
18368
+ outputField: "detected",
18369
+ defaultAggregate: "any",
18370
+ accept: [{
18371
+ cap: "motion",
18372
+ fieldPath: "detected"
18373
+ }],
18374
+ companions: [{
18375
+ fieldPath: "lastDetectedAt",
18376
+ expr: "null"
18377
+ }, {
18378
+ fieldPath: "autoClearAfterMs",
18379
+ expr: "null"
18380
+ }]
18381
+ }),
18382
+ binary({
18383
+ id: "contact",
18384
+ summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
18385
+ role: DeviceRole.ContactSensor,
18386
+ outputCap: "contact",
18387
+ outputField: "entryOpen",
18388
+ defaultAggregate: "any",
18389
+ accept: [{
18390
+ cap: "contact",
18391
+ fieldPath: "entryOpen"
18392
+ }],
18393
+ companions: [CHANGED]
18394
+ }),
18395
+ binary({
18396
+ id: "leak",
18397
+ summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
18398
+ role: DeviceRole.LeakSensor,
18399
+ outputCap: "flood",
18400
+ outputField: "flooded",
18401
+ defaultAggregate: "any",
18402
+ accept: [{
18403
+ cap: "flood",
18404
+ fieldPath: "flooded"
18405
+ }],
18406
+ companions: [CHANGED]
18407
+ }),
18408
+ binary({
18409
+ id: "smoke",
18410
+ summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
18411
+ role: DeviceRole.SmokeSensor,
18412
+ outputCap: "smoke",
18413
+ outputField: "detected",
18414
+ defaultAggregate: "any",
18415
+ accept: [{
18416
+ cap: "smoke",
18417
+ fieldPath: "detected"
18418
+ }],
18419
+ companions: [CHANGED]
18420
+ }),
18421
+ binary({
18422
+ id: "occupancy",
18423
+ summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
18424
+ role: DeviceRole.BinarySensor,
18425
+ outputCap: "binary",
18426
+ outputField: "on",
18427
+ defaultAggregate: "any",
18428
+ accept: [{
18429
+ cap: "motion",
18430
+ fieldPath: "detected"
18431
+ }, {
18432
+ cap: "binary",
18433
+ fieldPath: "on"
18434
+ }],
18435
+ companions: [CHANGED]
18436
+ }),
18437
+ binary({
18438
+ id: "binary",
18439
+ summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
18440
+ role: DeviceRole.BinarySensor,
18441
+ outputCap: "binary",
18442
+ outputField: "on",
18443
+ defaultAggregate: "any",
18444
+ accept: [{
18445
+ cap: "binary",
18446
+ fieldPath: "on"
18447
+ }],
18448
+ companions: [CHANGED]
18449
+ }),
18450
+ actuator({
18451
+ id: "switch",
18452
+ 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.",
18453
+ deviceType: DeviceType.Switch,
18454
+ outputCap: "switch",
18455
+ commandCaps: ["switch"]
18456
+ }),
18457
+ actuator({
18458
+ id: "light",
18459
+ 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.",
18460
+ deviceType: DeviceType.Light,
18461
+ outputCap: "switch",
18462
+ commandCaps: [
18463
+ "switch",
18464
+ "brightness",
18465
+ "color"
18466
+ ]
18467
+ })
18468
+ ];
18469
+ function actuator(input) {
18470
+ return {
18471
+ ...input,
18472
+ family: "actuator",
18473
+ role: void 0,
18474
+ memberDeviceType: input.deviceType,
18475
+ outputField: "on",
18476
+ quantity: "none",
18477
+ baseUnit: void 0,
18478
+ aggregates: BINARY_AGGREGATES,
18479
+ defaultAggregate: "any",
18480
+ empty: "offline",
18481
+ accept: [{
18482
+ cap: "switch",
18483
+ fieldPath: "on"
18484
+ }],
18485
+ companions: [CHANGED]
18486
+ };
18487
+ }
18488
+ new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
18489
+ DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
18102
18490
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
18103
18491
  /**
18104
18492
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -45088,6 +45476,8 @@ var NcSystemEventKindSchema = _enum([
45088
45476
  "detection-blind",
45089
45477
  "motion-noisy",
45090
45478
  "motion-calm",
45479
+ "package-llm-silent",
45480
+ "package-llm-answered",
45091
45481
  "recording-stalled",
45092
45482
  "recording-healthy",
45093
45483
  "recording-degraded",
@@ -56179,6 +56569,28 @@ var PackageVersionInfoSchema = object({
56179
56569
  deprecated: string().optional(),
56180
56570
  distTags: array(string()).readonly()
56181
56571
  });
56572
+ /**
56573
+ * One published document. `ok` is markdown from the registry. `missing` means
56574
+ * the registry answered and the file was not there. `error` is a registry or
56575
+ * tarball failure — never an empty string standing in for either.
56576
+ */
56577
+ var PackageDocBodySchema = discriminatedUnion("status", [
56578
+ object({
56579
+ status: literal("ok"),
56580
+ markdown: string()
56581
+ }),
56582
+ object({ status: literal("missing") }),
56583
+ object({
56584
+ status: literal("error"),
56585
+ message: string()
56586
+ })
56587
+ ]);
56588
+ var PackageDocsSchema = object({
56589
+ name: string(),
56590
+ version: string(),
56591
+ readme: PackageDocBodySchema,
56592
+ changelog: PackageDocBodySchema
56593
+ });
56182
56594
  var SearchResultSchema = object({
56183
56595
  name: string(),
56184
56596
  version: string(),
@@ -56397,7 +56809,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
56397
56809
  }), object({ success: literal(true) }), {
56398
56810
  kind: "mutation",
56399
56811
  auth: "admin"
56400
- }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
56812
+ }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
56813
+ name: string().min(1),
56814
+ version: string().min(1).optional()
56815
+ }), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
56401
56816
  kind: "mutation",
56402
56817
  auth: "admin"
56403
56818
  }), method(object({ packageName: string() }), object({ success: literal(true) }), {
@@ -59716,6 +60131,88 @@ var gasCapability = {
59716
60131
  * whether persisting is worth a SQLite commit. */
59717
60132
  volatileStateFields: ["lastChangedAt"]
59718
60133
  };
60134
+ var GroupCommandsSchema = object({
60135
+ /** One row per member that has been commanded. A later command replaces that member's row. */
60136
+ results: array(object({
60137
+ deviceId: number().int().nonnegative(),
60138
+ cap: string().min(1),
60139
+ method: string().min(1),
60140
+ ok: boolean(),
60141
+ error: string().optional(),
60142
+ /** Ms epoch of the attempt. */
60143
+ at: number()
60144
+ })) });
60145
+ var groupCommandsCapability = {
60146
+ name: "group-commands",
60147
+ scope: "device",
60148
+ deviceNative: true,
60149
+ mode: "singleton",
60150
+ deviceTypes: [DeviceType.Switch, DeviceType.Light],
60151
+ methods: {},
60152
+ status: {
60153
+ schema: GroupCommandsSchema,
60154
+ kind: "push"
60155
+ },
60156
+ runtimeState: GroupCommandsSchema,
60157
+ /**
60158
+ * Runtime-state durability: **session** — the last attempt. A restored
60159
+ * failure would outlive the command that caused it.
60160
+ *
60161
+ * See `RuntimeStateDurability`. Enforced by
60162
+ * `scripts/check-runtime-state-durability.ts`.
60163
+ */
60164
+ durability: "session",
60165
+ history: "none"
60166
+ };
60167
+ /**
60168
+ * How many members of a helper group answered (D944). Counts are integers,
60169
+ * not measurements: zero included is a real count, never a stand-in reading.
60170
+ * The reading itself stays on the output capability. History is none — the
60171
+ * series that later statistics read is the output cap's own history block.
60172
+ */
60173
+ var GroupStatusSchema = object({
60174
+ /** Members whose current value entered the aggregate. */
60175
+ included: number().int().nonnegative().meta(readingMeta("count", {
60176
+ precision: 0,
60177
+ label: "Reporting"
60178
+ })),
60179
+ /** Members left out: offline, unknown, or not a finite reading (D393). */
60180
+ excluded: number().int().nonnegative().meta(readingMeta("count", {
60181
+ precision: 0,
60182
+ label: "Excluded"
60183
+ })),
60184
+ /** `included` + `excluded`. */
60185
+ total: number().int().nonnegative().meta(readingMeta("count", {
60186
+ precision: 0,
60187
+ label: "Members"
60188
+ }))
60189
+ });
60190
+ var groupStatusCapability = {
60191
+ name: "group-status",
60192
+ scope: "device",
60193
+ deviceNative: true,
60194
+ mode: "singleton",
60195
+ deviceTypes: [
60196
+ DeviceType.Sensor,
60197
+ DeviceType.Switch,
60198
+ DeviceType.Light
60199
+ ],
60200
+ methods: {},
60201
+ status: {
60202
+ schema: GroupStatusSchema,
60203
+ kind: "push"
60204
+ },
60205
+ runtimeState: GroupStatusSchema,
60206
+ /**
60207
+ * Runtime-state durability: **session** — recomputed from members on the
60208
+ * next source event; a restored count would outlive the readings it named.
60209
+ *
60210
+ * See `RuntimeStateDurability`. Enforced by
60211
+ * `scripts/check-runtime-state-durability.ts`.
60212
+ */
60213
+ durability: "session",
60214
+ history: "none"
60215
+ };
59719
60216
  var HeatingValveStatusSchema = object({
59720
60217
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
59721
60218
  position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -61485,12 +61982,18 @@ var motionCapability = {
61485
61982
  methods: {
61486
61983
  /**
61487
61984
  * Pull the current motion state synchronously. Convenience shortcut
61488
- * for consumers that don't need the full status object; equivalent
61489
- * to `getStatus()?.detected ?? false`. Will likely be folded into
61490
- * `getStatus` once the auto-injected status surface lands in every
61491
- * consumer.
61985
+ * for consumers that don't need the full status object. A composed
61986
+ * group answers from its slice (D944): unknown throws, it is not off.
61492
61987
  */
61493
61988
  isDetected: method(object({ deviceId: number() }), boolean()) },
61989
+ /**
61990
+ * A composed motion group serves `isDetected` from `detected` (D944).
61991
+ * Without this the cap stays uncomposable (`has-commands`).
61992
+ */
61993
+ composedMethods: { isDetected: {
61994
+ kind: "slice",
61995
+ field: "detected"
61996
+ } },
61494
61997
  events: {
61495
61998
  /**
61496
61999
  * Fires every time the runner transitions a camera between
@@ -69681,6 +70184,8 @@ var DEVICE_LOCAL_STATE_CAPS = {
69681
70184
  featureProbe: featureProbeCapability,
69682
70185
  flood: floodCapability,
69683
70186
  gas: gasCapability,
70187
+ groupCommands: groupCommandsCapability,
70188
+ groupStatus: groupStatusCapability,
69684
70189
  heatingValve: heatingValveCapability,
69685
70190
  humidifier: humidifierCapability,
69686
70191
  humiditySensor: humiditySensorCapability,
@@ -71057,6 +71562,13 @@ Object.freeze({
71057
71562
  access: "view",
71058
71563
  role: "admin"
71059
71564
  },
71565
+ "addons.getPackageDocs": {
71566
+ capName: "addons",
71567
+ capScope: "system",
71568
+ addonId: null,
71569
+ access: "view",
71570
+ role: "admin"
71571
+ },
71060
71572
  "addons.getVersions": {
71061
71573
  capName: "addons",
71062
71574
  capScope: "system",
@@ -74165,6 +74677,20 @@ Object.freeze({
74165
74677
  access: "view",
74166
74678
  role: "viewer"
74167
74679
  },
74680
+ "groupCommands.getStatus": {
74681
+ capName: "group-commands",
74682
+ capScope: "device",
74683
+ addonId: null,
74684
+ access: "view",
74685
+ role: "viewer"
74686
+ },
74687
+ "groupStatus.getStatus": {
74688
+ capName: "group-status",
74689
+ capScope: "device",
74690
+ addonId: null,
74691
+ access: "view",
74692
+ role: "viewer"
74693
+ },
74168
74694
  "heatingValve.getStatus": {
74169
74695
  capName: "heating-valve",
74170
74696
  capScope: "device",
@@ -83987,6 +84513,37 @@ var SENSOR_CAP_VOCABULARY = {
83987
84513
  icon: "flame"
83988
84514
  }]
83989
84515
  },
84516
+ "group-commands": {
84517
+ scope: "device",
84518
+ noun: t("Group command", "Comando di gruppo")
84519
+ },
84520
+ "group-status": {
84521
+ scope: "device",
84522
+ noun: t("Group", "Gruppo"),
84523
+ state: [
84524
+ {
84525
+ kind: "numeric",
84526
+ field: "included",
84527
+ quantity: t("the members reporting", "i membri che rispondono"),
84528
+ label: t("Reporting", "Rispondono"),
84529
+ icon: "users"
84530
+ },
84531
+ {
84532
+ kind: "numeric",
84533
+ field: "excluded",
84534
+ quantity: t("the members left out", "i membri esclusi"),
84535
+ label: t("Excluded", "Esclusi"),
84536
+ icon: "users"
84537
+ },
84538
+ {
84539
+ kind: "numeric",
84540
+ field: "total",
84541
+ quantity: t("the member count", "il numero di membri"),
84542
+ label: t("Members", "Membri"),
84543
+ icon: "sigma"
84544
+ }
84545
+ ]
84546
+ },
83990
84547
  "humidity-sensor": {
83991
84548
  scope: "device",
83992
84549
  noun: t("Humidity", "Umidità"),
@@ -85452,6 +86009,16 @@ Object.freeze({
85452
86009
  form: "single",
85453
86010
  optional: false
85454
86011
  }],
86012
+ "groupCommands.getStatus": [{
86013
+ name: "deviceId",
86014
+ form: "single",
86015
+ optional: false
86016
+ }],
86017
+ "groupStatus.getStatus": [{
86018
+ name: "deviceId",
86019
+ form: "single",
86020
+ optional: false
86021
+ }],
85455
86022
  "heatingValve.getStatus": [{
85456
86023
  name: "deviceId",
85457
86024
  form: "single",
@@ -87436,6 +88003,8 @@ new Map(Object.entries({
87436
88003
  "detection-blind": "device-events",
87437
88004
  "motion-noisy": "device-events",
87438
88005
  "motion-calm": "device-events",
88006
+ "package-llm-silent": "device-events",
88007
+ "package-llm-answered": "device-events",
87439
88008
  "recording-stalled": "device-events",
87440
88009
  "recording-healthy": "device-events",
87441
88010
  "recording-degraded": "device-events",
@@ -87519,6 +88088,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
87519
88088
  label: "Motion without a subject",
87520
88089
  kinds: ["motion-noisy", "motion-calm"]
87521
88090
  },
88091
+ {
88092
+ id: "package-llm",
88093
+ family: "device-events",
88094
+ label: "Package confirm",
88095
+ kinds: ["package-llm-silent", "package-llm-answered"]
88096
+ },
87522
88097
  {
87523
88098
  id: "recording",
87524
88099
  family: "device-events",
@@ -87700,6 +88275,8 @@ var DEVICE_EVENTS = [
87700
88275
  "detection-blind",
87701
88276
  "motion-noisy",
87702
88277
  "motion-calm",
88278
+ "package-llm-silent",
88279
+ "package-llm-answered",
87703
88280
  "recording-stalled",
87704
88281
  "recording-healthy",
87705
88282
  "recording-degraded",
@@ -87968,6 +88545,15 @@ new Map([
87968
88545
  snapshotLabel: "Cameras with constant motion now",
87969
88546
  snapshotSection: null
87970
88547
  },
88548
+ {
88549
+ id: "package-llm-confirm",
88550
+ family: "device-events",
88551
+ label: "Package confirm (not answering / answering again)",
88552
+ bad: ["package-llm-silent"],
88553
+ recovery: ["package-llm-answered"],
88554
+ snapshotLabel: "Cameras whose package confirm is not answering",
88555
+ snapshotSection: null
88556
+ },
87971
88557
  {
87972
88558
  id: "recording-health",
87973
88559
  family: "device-events",