@camstack/types 1.2.416 → 1.2.418

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.
Files changed (79) hide show
  1. package/dist/addon.js +1 -1
  2. package/dist/addon.mjs +1 -1
  3. package/dist/capabilities/air-quality-sensor.cap.d.ts +2 -8
  4. package/dist/capabilities/ambient-light-sensor.cap.d.ts +3 -14
  5. package/dist/capabilities/battery.cap.d.ts +9 -2
  6. package/dist/capabilities/binary.cap.d.ts +0 -5
  7. package/dist/capabilities/capability-definition.d.ts +11 -6
  8. package/dist/capabilities/composer.cap.d.ts +198 -0
  9. package/dist/capabilities/consumables.cap.d.ts +30 -41
  10. package/dist/capabilities/control.cap.d.ts +3 -25
  11. package/dist/capabilities/core-blocks.cap.d.ts +1421 -0
  12. package/dist/capabilities/customize-catalog.d.ts +0 -8
  13. package/dist/capabilities/customize-spec.d.ts +2 -3
  14. package/dist/capabilities/device-manager.cap.d.ts +79 -0
  15. package/dist/capabilities/device-provider.cap.d.ts +36 -0
  16. package/dist/capabilities/enum-sensor.cap.d.ts +5 -17
  17. package/dist/capabilities/group-status.cap.d.ts +46 -0
  18. package/dist/capabilities/humidity-sensor.cap.d.ts +3 -14
  19. package/dist/capabilities/index.d.ts +4 -2
  20. package/dist/capabilities/motion.cap.d.ts +13 -5
  21. package/dist/capabilities/numeric-sensor.cap.d.ts +8 -16
  22. package/dist/capabilities/pet-feeder.cap.d.ts +1 -1
  23. package/dist/capabilities/power-meter.cap.d.ts +0 -11
  24. package/dist/capabilities/pressure-sensor.cap.d.ts +3 -9
  25. package/dist/capabilities/running-state.cap.d.ts +1 -2
  26. package/dist/capabilities/runtime-field-unit.d.ts +1 -0
  27. package/dist/capabilities/runtime-field-units.d.ts +8 -0
  28. package/dist/capabilities/switch.cap.d.ts +0 -5
  29. package/dist/capabilities/temperature-sensor.cap.d.ts +5 -20
  30. package/dist/capabilities/weather.cap.d.ts +4 -13
  31. package/dist/client.d.ts +2 -1
  32. package/dist/client.js +57 -5
  33. package/dist/client.mjs +55 -6
  34. package/dist/composition/composition-report.d.ts +80 -3
  35. package/dist/composition/composition.d.ts +285 -5
  36. package/dist/composition/helpers/aggregate.d.ts +41 -0
  37. package/dist/composition/helpers/catalog.d.ts +42 -0
  38. package/dist/composition/helpers/compile.d.ts +22 -0
  39. package/dist/composition/helpers/index.d.ts +6 -0
  40. package/dist/composition/helpers/save.d.ts +18 -0
  41. package/dist/composition/helpers/source-pick.d.ts +40 -0
  42. package/dist/composition/helpers/spec.d.ts +38 -0
  43. package/dist/composition/index.d.ts +1 -0
  44. package/dist/composition/source-ref.d.ts +11 -0
  45. package/dist/composition/state-fields.d.ts +1 -1
  46. package/dist/composition/validate-composition.d.ts +8 -2
  47. package/dist/{composition-kEl9mevu.mjs → composition-B85l9j1M.mjs} +114 -8
  48. package/dist/{composition-DsmkwP9-.js → composition-OJKOJJD-.js} +179 -7
  49. package/dist/device/cap-vocabulary/entries/sensors.d.ts +1 -1
  50. package/dist/device/cap-vocabulary/types.d.ts +2 -2
  51. package/dist/device/schema-fields.d.ts +3 -0
  52. package/dist/device/source-info.d.ts +12 -6
  53. package/dist/generated/addon-api.d.ts +14 -0
  54. package/dist/generated/addon-cap-facts.d.ts +1 -1
  55. package/dist/generated/cap-status-types.d.ts +3 -1
  56. package/dist/generated/capability-router-map.d.ts +5 -2
  57. package/dist/generated/customize-catalog.d.ts +11 -87
  58. package/dist/generated/device-local-state.d.ts +3 -0
  59. package/dist/generated/device-proxy.d.ts +3 -0
  60. package/dist/generated/history-catalog.d.ts +1 -1
  61. package/dist/generated/method-access-map.d.ts +1 -1
  62. package/dist/generated/method-device-selectors.d.ts +1 -1
  63. package/dist/generated/runtime-state-durability.d.ts +1 -1
  64. package/dist/index.d.ts +3 -1
  65. package/dist/index.js +1458 -499
  66. package/dist/index.mjs +1420 -499
  67. package/dist/node.js +1 -1
  68. package/dist/node.mjs +1 -1
  69. package/dist/{sleep-BPQ2ZyaI.js → sleep-7Gw3GJ4n.js} +232 -127
  70. package/dist/{sleep-BErAxhul.mjs → sleep-B81gwQuR.mjs} +179 -128
  71. package/dist/units/convert.d.ts +5 -0
  72. package/dist/units/format-reading.d.ts +15 -0
  73. package/dist/units/index.d.ts +8 -1
  74. package/dist/units/ingest-reading.d.ts +21 -0
  75. package/dist/units/quantity.d.ts +47 -0
  76. package/dist/units/threshold.d.ts +6 -0
  77. package/dist/units/unit-table.d.ts +2 -228
  78. package/package.json +1 -1
  79. package/dist/capabilities/advanced-parameters.d.ts +0 -17
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The request the common source selector (#236) accepts for a helper (D944 §5).
3
+ * The selector's layout is not specified here. Groups ask for one device type
4
+ * and the catalog field; they do not opt into incompatible fields.
5
+ */
6
+ import type { DeviceType } from '../../device/device-type.js';
7
+ import type { CompositionSourceRef } from '../source-ref.js';
8
+ import type { GroupTemplate } from './catalog.js';
9
+ export interface SourcePickAccept {
10
+ readonly cap: string;
11
+ readonly fieldPath: string;
12
+ readonly valueKind: 'number' | 'boolean';
13
+ }
14
+ export interface SourcePickHistory {
15
+ readonly required: boolean;
16
+ readonly minRetentionMs: number | null;
17
+ }
18
+ export interface SourcePickRequest {
19
+ readonly purpose: 'member' | 'reading' | 'historised';
20
+ readonly multiple: boolean;
21
+ readonly max: number;
22
+ readonly deviceTypes: readonly DeviceType[];
23
+ readonly accept: readonly SourcePickAccept[];
24
+ readonly history: SourcePickHistory;
25
+ readonly mustBindCaps: readonly string[];
26
+ readonly exclude: readonly CompositionSourceRef[];
27
+ }
28
+ export interface SourcePick {
29
+ readonly source: CompositionSourceRef;
30
+ readonly deviceId: number;
31
+ readonly parentName: string | null;
32
+ readonly cap: string;
33
+ readonly fieldPath: string;
34
+ /** Current reading. `null` is unknown, never a stand-in 0. */
35
+ readonly current: number | boolean | string | null;
36
+ readonly compatibility: 'direct' | 'convert' | 'incompatible';
37
+ readonly history: 'recorded' | 'absent' | 'unknown';
38
+ }
39
+ /** What the member step asks the selector for this template. */
40
+ export declare function sourcePickForTemplate(template: GroupTemplate): SourcePickRequest;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * A helper spec is what the editor stores and reads back (D944). The compiled
3
+ * features are a projection; the editor never edits the expression.
4
+ */
5
+ import { z } from 'zod';
6
+ import { CompositionSourceRefSchema } from '../source-ref.js';
7
+ /** A group may name more members than an expression call (the 16-arg cap). */
8
+ export declare const MAX_HELPER_MEMBERS: number;
9
+ export declare const GROUP_TEMPLATE_IDS: readonly ['temperature', 'humidity', 'pressure', 'illuminance', 'power', 'energy', 'battery', 'numeric', 'motion', 'contact', 'leak', 'smoke', 'occupancy', 'binary'];
10
+ export type GroupTemplateId = (typeof GROUP_TEMPLATE_IDS)[number];
11
+ export declare const NUMERIC_AGGREGATES: readonly ['mean', 'median', 'min', 'max', 'sum'];
12
+ export type NumericAggregate = (typeof NUMERIC_AGGREGATES)[number];
13
+ export declare const BINARY_AGGREGATES: readonly ['any', 'all'];
14
+ export type BinaryAggregate = (typeof BINARY_AGGREGATES)[number];
15
+ export declare const AGGREGATE_REDUCERS: readonly ['mean', 'median', 'min', 'max', 'sum', 'any', 'all'];
16
+ export type AggregateReducer = (typeof AGGREGATE_REDUCERS)[number];
17
+ export declare const GroupTemplateIdSchema: z.ZodType<GroupTemplateId>;
18
+ export declare const AggregateReducerSchema: z.ZodType<AggregateReducer>;
19
+ /** One member. The field is stored so occupancy can name motion or binary. */
20
+ export declare const HelperMemberSchema: z.ZodObject<{
21
+ source: typeof CompositionSourceRefSchema;
22
+ cap: z.ZodString;
23
+ fieldPath: z.ZodString;
24
+ }>;
25
+ export type HelperMember = z.infer<typeof HelperMemberSchema>;
26
+ /**
27
+ * Phase 1a is groups only. Math, integral and statistics stay off this union
28
+ * until the composer can run them — a control must not save a spec it cannot.
29
+ */
30
+ export declare const GroupHelperSpecSchema: z.ZodObject<{
31
+ kind: z.ZodLiteral<'group'>;
32
+ templateId: typeof GroupTemplateIdSchema;
33
+ aggregate: typeof AggregateReducerSchema;
34
+ members: z.ZodArray<typeof HelperMemberSchema>;
35
+ }>;
36
+ export type GroupHelperSpec = z.infer<typeof GroupHelperSpecSchema>;
37
+ export declare const HelperSpecSchema: z.ZodDiscriminatedUnion<[typeof GroupHelperSpecSchema], 'kind'>;
38
+ export type HelperSpec = z.infer<typeof HelperSpecSchema>;
@@ -3,6 +3,7 @@
3
3
  * inside @camstack/types import the deep modules, never this file.
4
4
  */
5
5
  export * from './composable-caps.js';
6
+ export * from './helpers/index.js';
6
7
  export * from './composition.js';
7
8
  export * from './composition-events.js';
8
9
  export * from './composition-graph.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A composition source identity: `(addonId, stableId)`, never a numeric device
3
+ * id. Split out so helper schemas can name it without a cycle through
4
+ * `composition.ts` (D659).
5
+ */
6
+ import { z } from 'zod';
7
+ export declare const CompositionSourceRefSchema: z.ZodObject<{
8
+ addonId: z.ZodString;
9
+ stableId: z.ZodString;
10
+ }>;
11
+ export type CompositionSourceRef = z.infer<typeof CompositionSourceRefSchema>;
@@ -20,6 +20,6 @@ export declare function requiredTopLevelKeys(schema: z.ZodType): readonly string
20
20
  /**
21
21
  * The proper prefixes of `path` whose schema accepts `undefined`: a required
22
22
  * leaf under one of them is required only once something writes that parent
23
- * (`remaining.unit` is required only when `remaining` is present).
23
+ * (`remaining.kind` is required only when `remaining` is present).
24
24
  */
25
25
  export declare function optionalAncestors(schema: z.ZodType, path: string): readonly string[];
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import type { CapabilityDefinition } from '../capabilities/capability-definition.js';
12
12
  import type { DeviceType } from '../device/device-type.js';
13
+ import type { ReadingQuantity } from '../units/quantity.js';
13
14
  import { type Composition, type CompositionSourceRef } from './composition.js';
14
15
  import { type CompositionPlanResult } from './plan-result.js';
15
16
  import { type CompositionPeer } from './composition-graph.js';
@@ -22,6 +23,10 @@ export interface CompositionSourceFound {
22
23
  readonly kind: 'found';
23
24
  readonly deviceId: number;
24
25
  readonly caps: ReadonlySet<string>;
26
+ /** Present when the row was read. A helper group requires this one type. */
27
+ readonly deviceType?: DeviceType;
28
+ /** Present when the device declares one. Mixed quantities are refused. */
29
+ readonly quantity?: ReadingQuantity;
25
30
  }
26
31
  export interface CompositionSourceAbsent {
27
32
  readonly kind: 'absent';
@@ -67,8 +72,9 @@ export type { CompositionPlanResult } from './plan-result.js';
67
72
  export declare const PENDING_BLOCK_ID = "(unsaved)";
68
73
  /**
69
74
  * The static plan. `target` describes an EXISTING target device (one read);
70
- * it is ignored for a new one. On an existing target each feature REPLACES a
71
- * cap the device has natively, else ADDS it (D663).
75
+ * it is ignored for a new one. On an existing target each feature ADDS a cap
76
+ * the device lacks. A feature that would replace a native capability is
77
+ * refused by name (D944). A new device is unchanged.
72
78
  */
73
79
  export declare function planComposition(composition: Composition, lookupCap: CapabilityLookup, target?: CompositionTargetDescription): CompositionPlanResult;
74
80
  export declare function compositionSourceRefs(composition: Composition): readonly CompositionSourceRef[];
@@ -2160,6 +2160,78 @@ function evaluateExpressionSource(expr, bindingValues, now, opts) {
2160
2160
  }
2161
2161
  }
2162
2162
  //#endregion
2163
+ //#region src/composition/source-ref.ts
2164
+ /**
2165
+ * A composition source identity: `(addonId, stableId)`, never a numeric device
2166
+ * id. Split out so helper schemas can name it without a cycle through
2167
+ * `composition.ts` (D659).
2168
+ */
2169
+ var CompositionSourceRefSchema = z.object({
2170
+ /** The owning addon; `stableId` is unique only within it. */
2171
+ addonId: z.string().min(1),
2172
+ stableId: z.string().min(1)
2173
+ });
2174
+ //#endregion
2175
+ //#region src/composition/helpers/spec.ts
2176
+ /**
2177
+ * A helper spec is what the editor stores and reads back (D944). The compiled
2178
+ * features are a projection; the editor never edits the expression.
2179
+ */
2180
+ /** A group may name more members than an expression call (the 16-arg cap). */
2181
+ var MAX_HELPER_MEMBERS = 64;
2182
+ var GROUP_TEMPLATE_IDS = [
2183
+ "temperature",
2184
+ "humidity",
2185
+ "pressure",
2186
+ "illuminance",
2187
+ "power",
2188
+ "energy",
2189
+ "battery",
2190
+ "numeric",
2191
+ "motion",
2192
+ "contact",
2193
+ "leak",
2194
+ "smoke",
2195
+ "occupancy",
2196
+ "binary"
2197
+ ];
2198
+ var NUMERIC_AGGREGATES = [
2199
+ "mean",
2200
+ "median",
2201
+ "min",
2202
+ "max",
2203
+ "sum"
2204
+ ];
2205
+ var BINARY_AGGREGATES = ["any", "all"];
2206
+ var AGGREGATE_REDUCERS = [
2207
+ "mean",
2208
+ "median",
2209
+ "min",
2210
+ "max",
2211
+ "sum",
2212
+ "any",
2213
+ "all"
2214
+ ];
2215
+ var GroupTemplateIdSchema = z.enum(GROUP_TEMPLATE_IDS);
2216
+ var AggregateReducerSchema = z.enum(AGGREGATE_REDUCERS);
2217
+ /** One member. The field is stored so occupancy can name motion or binary. */
2218
+ var HelperMemberSchema = z.object({
2219
+ source: CompositionSourceRefSchema,
2220
+ cap: z.string().min(1),
2221
+ fieldPath: z.string().min(1)
2222
+ });
2223
+ /**
2224
+ * Phase 1a is groups only. Math, integral and statistics stay off this union
2225
+ * until the composer can run them — a control must not save a spec it cannot.
2226
+ */
2227
+ var GroupHelperSpecSchema = z.object({
2228
+ kind: z.literal("group"),
2229
+ templateId: GroupTemplateIdSchema,
2230
+ aggregate: AggregateReducerSchema,
2231
+ members: z.array(HelperMemberSchema).min(1).max(64)
2232
+ });
2233
+ var HelperSpecSchema = z.discriminatedUnion("kind", [GroupHelperSpecSchema]);
2234
+ //#endregion
2163
2235
  //#region src/composition/composition.ts
2164
2236
  /**
2165
2237
  * A COMPOSITION: a device assembled from other devices' state (D659).
@@ -2195,11 +2267,6 @@ var MAX_COMPOSITION_FEATURES = 16;
2195
2267
  var MAX_COMPOSITION_FIELDS_PER_FEATURE = 32;
2196
2268
  /** Longest snippet a `code` source may carry (slice 4). */
2197
2269
  var MAX_COMPOSITION_CODE_LENGTH = 2e4;
2198
- var CompositionSourceRefSchema = z.object({
2199
- /** The owning addon; `stableId` is unique only within it. */
2200
- addonId: z.string().min(1),
2201
- stableId: z.string().min(1)
2202
- });
2203
2270
  /** One field of one capability of one source device. */
2204
2271
  var CompositionFieldReadSchema = z.object({
2205
2272
  source: CompositionSourceRefSchema,
@@ -2242,10 +2309,42 @@ var CompositionCodeSourceSchema = z.object({
2242
2309
  kind: z.literal("code"),
2243
2310
  code: z.string().min(1).max(MAX_COMPOSITION_CODE_LENGTH)
2244
2311
  });
2312
+ /**
2313
+ * `aggregate`: a group reducer. Offline and unknown reads are excluded, not
2314
+ * folded in as 0 or false (D944). Free expressions stay `expression` so a
2315
+ * stale member is still included there.
2316
+ */
2317
+ var CompositionAggregateSourceSchema = z.object({
2318
+ kind: z.literal("aggregate"),
2319
+ reducer: z.enum([
2320
+ "mean",
2321
+ "median",
2322
+ "min",
2323
+ "max",
2324
+ "sum",
2325
+ "any",
2326
+ "all"
2327
+ ]),
2328
+ reads: z.array(CompositionFieldReadSchema).min(1).max(64),
2329
+ /** `value` is the reading; the others are the member counts. */
2330
+ publish: z.enum([
2331
+ "value",
2332
+ "included",
2333
+ "excluded",
2334
+ "total"
2335
+ ]),
2336
+ /** What a `value` publish does when nobody is included. Counts ignore this. */
2337
+ empty: z.enum([
2338
+ "null",
2339
+ "omit",
2340
+ "offline"
2341
+ ])
2342
+ });
2245
2343
  var CompositionFieldSourceSchema = z.discriminatedUnion("kind", [
2246
2344
  CompositionFromSourceSchema,
2247
2345
  CompositionExpressionSourceSchema,
2248
- CompositionCodeSourceSchema
2346
+ CompositionCodeSourceSchema,
2347
+ CompositionAggregateSourceSchema
2249
2348
  ]);
2250
2349
  /** `forward`: RESERVED for slice 3. Argument mapping arrives there as an additive optional `args`. */
2251
2350
  var CompositionForwardCommandSchema = z.object({
@@ -2311,7 +2410,13 @@ var CompositionExistingTargetSchema = z.object({
2311
2410
  var CompositionTargetSchema = z.discriminatedUnion("kind", [CompositionNewTargetSchema, CompositionExistingTargetSchema]);
2312
2411
  var CompositionSchema = z.object({
2313
2412
  target: CompositionTargetSchema,
2314
- features: z.array(CompositionFeatureSchema).min(1).max(16)
2413
+ features: z.array(CompositionFeatureSchema).min(1).max(16),
2414
+ /**
2415
+ * Present when the block is a helper template. Absent on a free
2416
+ * composition (the room example, device 4603). The editor reads this,
2417
+ * never the compiled features.
2418
+ */
2419
+ helper: HelperSpecSchema.optional()
2315
2420
  }).superRefine((composition, ctx) => {
2316
2421
  const seen = /* @__PURE__ */ new Set();
2317
2422
  composition.features.forEach((feature, index) => {
@@ -2358,6 +2463,7 @@ function fieldReads(source) {
2358
2463
  cap: binding.cap,
2359
2464
  fieldPath: binding.fieldPath
2360
2465
  }] : []);
2466
+ case "aggregate": return source.reads;
2361
2467
  case "code": return [];
2362
2468
  }
2363
2469
  }
@@ -2384,4 +2490,4 @@ function featureSources(feature) {
2384
2490
  return featureFieldEntries(feature).map((e) => e.source);
2385
2491
  }
2386
2492
  //#endregion
2387
- export { MAX_EXPRESSION_AST_NODES as $, compositionFeatureEnabled as A, Fmp4BoxSplitter as At, createStatefulExpressionMemory as B, pickVideoEncoder as Bt, CompositionTargetSchema as C, FFMPEG_PATH_ENV as Ct, MAX_COMPOSITION_ITEMS as D, getFfmpegArtifact as Dt, MAX_COMPOSITION_FIELDS_PER_FEATURE as E, FFMPEG_RELEASE as Et, featureFieldEntries as F, buildInputArgs as Ft, createExpressionScope as G, evaluateStatefulExpressionCall as H, featureSources as I, buildVideoArgs as It, compileExpressionSafe as J, evaluateAst as K, fieldReads as L, invocationFromEncodeProfile as Lt, compositionReadKey as M, audioPlanFromEncodeProfile as Mt, compositionSliceKey as N, buildAudioArgs as Nt, composedDeviceRef as O, getFfmpegDownloadUrl as Ot, compositionSourceKey as P, buildFfmpegArgs as Pt, EXPRESSION_IDENTIFIER_RE as Q, EXPRESSION_INJECTED_NOW as R, isSoftwareDecode as Rt, CompositionSourceRefSchema as S, sliceChangedAt as St, MAX_COMPOSITION_FEATURES as T, ffmpegBinaryName as Tt, toExpressionValue as U, evaluateExpressionSource as V, resolveStreamMaps as Vt, validateExpressionSource as W, tokenize as X, parseExpression as Y, EXPRESSION_COMPILE_CACHE_CAPACITY as Z, CompositionItemEntrySchema as _, SOURCE_DEVICE_TYPES as _t, CUSTOMIZATION_BLOCK_NAME_PREFIX as a, EXPRESSION_BUILTINS as at, CompositionPassthroughFeatureSchema as b, isSourceCap as bt, CompositionCommandTargetSchema as c, evaluateCount as ct, CompositionFeatureSchema as d, ExpressionEvalError as dt, MAX_EXPRESSION_BINDINGS as et, CompositionFieldReadSchema as f, ExpressionParseError as ft, CompositionFromSourceSchema as g, SOURCE_CAP_CHANGED_AT_FIELD as gt, CompositionForwardCommandSchema as h, SOURCE_CAP_ACTIVE_FIELD as ht, COMPOSITION_ITEM_KEY_RE as i, RESERVED_BINDING_NAMES as it, compositionItemFieldPath as j, AUDIO_PRESETS as jt, composedDeviceStableId as k, canonicalHash as kt, CompositionExistingTargetSchema as l, evaluateRose as lt, CompositionFieldsFeatureSchema as m, SOURCE_CAPS as mt, COMPOSER_ADDON_ID as n, MAX_EXPRESSION_EVAL_STEPS as nt, CompositionBindingSchema as o, EXPRESSION_BUILTIN_NAMES as ot, CompositionFieldSourceSchema as p, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as pt, compileExpression as q, COMPOSITION_ITEM_ARRAY_PATH as r, MAX_EXPRESSION_SOURCE_LENGTH as rt, CompositionCodeSourceSchema as s, disarmRoseMemory as st, COMPOSED_DEVICE_STABLE_ID_PREFIX as t, MAX_EXPRESSION_CALL_ARGS as tt, CompositionExpressionSourceSchema as u, isStatefulBuiltin as ut, CompositionLiteralBindingSchema as v, evaluateSensorEdge as vt, MAX_COMPOSITION_CODE_LENGTH as w, chooseFfmpegBinary as wt, CompositionSchema as x, sliceActiveValue as xt, CompositionNewTargetSchema as y, evaluateSensorLevelEdge as yt, cloneStatefulExpressionMemory as z, logBannerArgs as zt };
2493
+ export { evaluateStatefulExpressionCall as $, compositionFeatureEnabled as A, evaluateSensorLevelEdge as At, BINARY_AGGREGATES as B, canonicalHash as Bt, CompositionTargetSchema as C, ExpressionParseError as Ct, MAX_COMPOSITION_ITEMS as D, SOURCE_CAP_CHANGED_AT_FIELD as Dt, MAX_COMPOSITION_FIELDS_PER_FEATURE as E, SOURCE_CAP_ACTIVE_FIELD as Et, featureFieldEntries as F, chooseFfmpegBinary as Ft, HelperSpecSchema as G, buildFfmpegArgs as Gt, GroupHelperSpecSchema as H, AUDIO_PRESETS as Ht, featureSources as I, ffmpegBinaryName as It, CompositionSourceRefSchema as J, invocationFromEncodeProfile as Jt, MAX_HELPER_MEMBERS as K, buildInputArgs as Kt, fieldReads as L, FFMPEG_RELEASE as Lt, compositionReadKey as M, sliceActiveValue as Mt, compositionSliceKey as N, sliceChangedAt as Nt, composedDeviceRef as O, SOURCE_DEVICE_TYPES as Ot, compositionSourceKey as P, FFMPEG_PATH_ENV as Pt, evaluateExpressionSource as Q, resolveStreamMaps as Qt, AGGREGATE_REDUCERS as R, getFfmpegArtifact as Rt, CompositionSchema as S, ExpressionEvalError as St, MAX_COMPOSITION_FEATURES as T, SOURCE_CAPS as Tt, GroupTemplateIdSchema as U, audioPlanFromEncodeProfile as Ut, GROUP_TEMPLATE_IDS as V, Fmp4BoxSplitter as Vt, HelperMemberSchema as W, buildAudioArgs as Wt, cloneStatefulExpressionMemory as X, logBannerArgs as Xt, EXPRESSION_INJECTED_NOW as Y, isSoftwareDecode as Yt, createStatefulExpressionMemory as Z, pickVideoEncoder as Zt, CompositionFromSourceSchema as _, EXPRESSION_BUILTIN_NAMES as _t, CUSTOMIZATION_BLOCK_NAME_PREFIX as a, compileExpressionSafe as at, CompositionNewTargetSchema as b, evaluateRose as bt, CompositionCodeSourceSchema as c, EXPRESSION_COMPILE_CACHE_CAPACITY as ct, CompositionExpressionSourceSchema as d, MAX_EXPRESSION_BINDINGS as dt, toExpressionValue as et, CompositionFeatureSchema as f, MAX_EXPRESSION_CALL_ARGS as ft, CompositionForwardCommandSchema as g, EXPRESSION_BUILTINS as gt, CompositionFieldsFeatureSchema as h, RESERVED_BINDING_NAMES as ht, COMPOSITION_ITEM_KEY_RE as i, compileExpression as it, compositionItemFieldPath as j, isSourceCap as jt, composedDeviceStableId as k, evaluateSensorEdge as kt, CompositionCommandTargetSchema as l, EXPRESSION_IDENTIFIER_RE as lt, CompositionFieldSourceSchema as m, MAX_EXPRESSION_SOURCE_LENGTH as mt, COMPOSER_ADDON_ID as n, createExpressionScope as nt, CompositionAggregateSourceSchema as o, parseExpression as ot, CompositionFieldReadSchema as p, MAX_EXPRESSION_EVAL_STEPS as pt, NUMERIC_AGGREGATES as q, buildVideoArgs as qt, COMPOSITION_ITEM_ARRAY_PATH as r, evaluateAst as rt, CompositionBindingSchema as s, tokenize as st, COMPOSED_DEVICE_STABLE_ID_PREFIX as t, validateExpressionSource as tt, CompositionExistingTargetSchema as u, MAX_EXPRESSION_AST_NODES as ut, CompositionItemEntrySchema as v, disarmRoseMemory as vt, MAX_COMPOSITION_CODE_LENGTH as w, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as wt, CompositionPassthroughFeatureSchema as x, isStatefulBuiltin as xt, CompositionLiteralBindingSchema as y, evaluateCount as yt, AggregateReducerSchema as z, getFfmpegDownloadUrl as zt };
@@ -2160,6 +2160,78 @@ function evaluateExpressionSource(expr, bindingValues, now, opts) {
2160
2160
  }
2161
2161
  }
2162
2162
  //#endregion
2163
+ //#region src/composition/source-ref.ts
2164
+ /**
2165
+ * A composition source identity: `(addonId, stableId)`, never a numeric device
2166
+ * id. Split out so helper schemas can name it without a cycle through
2167
+ * `composition.ts` (D659).
2168
+ */
2169
+ var CompositionSourceRefSchema = zod.z.object({
2170
+ /** The owning addon; `stableId` is unique only within it. */
2171
+ addonId: zod.z.string().min(1),
2172
+ stableId: zod.z.string().min(1)
2173
+ });
2174
+ //#endregion
2175
+ //#region src/composition/helpers/spec.ts
2176
+ /**
2177
+ * A helper spec is what the editor stores and reads back (D944). The compiled
2178
+ * features are a projection; the editor never edits the expression.
2179
+ */
2180
+ /** A group may name more members than an expression call (the 16-arg cap). */
2181
+ var MAX_HELPER_MEMBERS = 64;
2182
+ var GROUP_TEMPLATE_IDS = [
2183
+ "temperature",
2184
+ "humidity",
2185
+ "pressure",
2186
+ "illuminance",
2187
+ "power",
2188
+ "energy",
2189
+ "battery",
2190
+ "numeric",
2191
+ "motion",
2192
+ "contact",
2193
+ "leak",
2194
+ "smoke",
2195
+ "occupancy",
2196
+ "binary"
2197
+ ];
2198
+ var NUMERIC_AGGREGATES = [
2199
+ "mean",
2200
+ "median",
2201
+ "min",
2202
+ "max",
2203
+ "sum"
2204
+ ];
2205
+ var BINARY_AGGREGATES = ["any", "all"];
2206
+ var AGGREGATE_REDUCERS = [
2207
+ "mean",
2208
+ "median",
2209
+ "min",
2210
+ "max",
2211
+ "sum",
2212
+ "any",
2213
+ "all"
2214
+ ];
2215
+ var GroupTemplateIdSchema = zod.z.enum(GROUP_TEMPLATE_IDS);
2216
+ var AggregateReducerSchema = zod.z.enum(AGGREGATE_REDUCERS);
2217
+ /** One member. The field is stored so occupancy can name motion or binary. */
2218
+ var HelperMemberSchema = zod.z.object({
2219
+ source: CompositionSourceRefSchema,
2220
+ cap: zod.z.string().min(1),
2221
+ fieldPath: zod.z.string().min(1)
2222
+ });
2223
+ /**
2224
+ * Phase 1a is groups only. Math, integral and statistics stay off this union
2225
+ * until the composer can run them — a control must not save a spec it cannot.
2226
+ */
2227
+ var GroupHelperSpecSchema = zod.z.object({
2228
+ kind: zod.z.literal("group"),
2229
+ templateId: GroupTemplateIdSchema,
2230
+ aggregate: AggregateReducerSchema,
2231
+ members: zod.z.array(HelperMemberSchema).min(1).max(64)
2232
+ });
2233
+ var HelperSpecSchema = zod.z.discriminatedUnion("kind", [GroupHelperSpecSchema]);
2234
+ //#endregion
2163
2235
  //#region src/composition/composition.ts
2164
2236
  /**
2165
2237
  * A COMPOSITION: a device assembled from other devices' state (D659).
@@ -2195,11 +2267,6 @@ var MAX_COMPOSITION_FEATURES = 16;
2195
2267
  var MAX_COMPOSITION_FIELDS_PER_FEATURE = 32;
2196
2268
  /** Longest snippet a `code` source may carry (slice 4). */
2197
2269
  var MAX_COMPOSITION_CODE_LENGTH = 2e4;
2198
- var CompositionSourceRefSchema = zod.z.object({
2199
- /** The owning addon; `stableId` is unique only within it. */
2200
- addonId: zod.z.string().min(1),
2201
- stableId: zod.z.string().min(1)
2202
- });
2203
2270
  /** One field of one capability of one source device. */
2204
2271
  var CompositionFieldReadSchema = zod.z.object({
2205
2272
  source: CompositionSourceRefSchema,
@@ -2242,10 +2309,42 @@ var CompositionCodeSourceSchema = zod.z.object({
2242
2309
  kind: zod.z.literal("code"),
2243
2310
  code: zod.z.string().min(1).max(MAX_COMPOSITION_CODE_LENGTH)
2244
2311
  });
2312
+ /**
2313
+ * `aggregate`: a group reducer. Offline and unknown reads are excluded, not
2314
+ * folded in as 0 or false (D944). Free expressions stay `expression` so a
2315
+ * stale member is still included there.
2316
+ */
2317
+ var CompositionAggregateSourceSchema = zod.z.object({
2318
+ kind: zod.z.literal("aggregate"),
2319
+ reducer: zod.z.enum([
2320
+ "mean",
2321
+ "median",
2322
+ "min",
2323
+ "max",
2324
+ "sum",
2325
+ "any",
2326
+ "all"
2327
+ ]),
2328
+ reads: zod.z.array(CompositionFieldReadSchema).min(1).max(64),
2329
+ /** `value` is the reading; the others are the member counts. */
2330
+ publish: zod.z.enum([
2331
+ "value",
2332
+ "included",
2333
+ "excluded",
2334
+ "total"
2335
+ ]),
2336
+ /** What a `value` publish does when nobody is included. Counts ignore this. */
2337
+ empty: zod.z.enum([
2338
+ "null",
2339
+ "omit",
2340
+ "offline"
2341
+ ])
2342
+ });
2245
2343
  var CompositionFieldSourceSchema = zod.z.discriminatedUnion("kind", [
2246
2344
  CompositionFromSourceSchema,
2247
2345
  CompositionExpressionSourceSchema,
2248
- CompositionCodeSourceSchema
2346
+ CompositionCodeSourceSchema,
2347
+ CompositionAggregateSourceSchema
2249
2348
  ]);
2250
2349
  /** `forward`: RESERVED for slice 3. Argument mapping arrives there as an additive optional `args`. */
2251
2350
  var CompositionForwardCommandSchema = zod.z.object({
@@ -2311,7 +2410,13 @@ var CompositionExistingTargetSchema = zod.z.object({
2311
2410
  var CompositionTargetSchema = zod.z.discriminatedUnion("kind", [CompositionNewTargetSchema, CompositionExistingTargetSchema]);
2312
2411
  var CompositionSchema = zod.z.object({
2313
2412
  target: CompositionTargetSchema,
2314
- features: zod.z.array(CompositionFeatureSchema).min(1).max(16)
2413
+ features: zod.z.array(CompositionFeatureSchema).min(1).max(16),
2414
+ /**
2415
+ * Present when the block is a helper template. Absent on a free
2416
+ * composition (the room example, device 4603). The editor reads this,
2417
+ * never the compiled features.
2418
+ */
2419
+ helper: HelperSpecSchema.optional()
2315
2420
  }).superRefine((composition, ctx) => {
2316
2421
  const seen = /* @__PURE__ */ new Set();
2317
2422
  composition.features.forEach((feature, index) => {
@@ -2358,6 +2463,7 @@ function fieldReads(source) {
2358
2463
  cap: binding.cap,
2359
2464
  fieldPath: binding.fieldPath
2360
2465
  }] : []);
2466
+ case "aggregate": return source.reads;
2361
2467
  case "code": return [];
2362
2468
  }
2363
2469
  }
@@ -2384,12 +2490,30 @@ function featureSources(feature) {
2384
2490
  return featureFieldEntries(feature).map((e) => e.source);
2385
2491
  }
2386
2492
  //#endregion
2493
+ Object.defineProperty(exports, "AGGREGATE_REDUCERS", {
2494
+ enumerable: true,
2495
+ get: function() {
2496
+ return AGGREGATE_REDUCERS;
2497
+ }
2498
+ });
2387
2499
  Object.defineProperty(exports, "AUDIO_PRESETS", {
2388
2500
  enumerable: true,
2389
2501
  get: function() {
2390
2502
  return AUDIO_PRESETS;
2391
2503
  }
2392
2504
  });
2505
+ Object.defineProperty(exports, "AggregateReducerSchema", {
2506
+ enumerable: true,
2507
+ get: function() {
2508
+ return AggregateReducerSchema;
2509
+ }
2510
+ });
2511
+ Object.defineProperty(exports, "BINARY_AGGREGATES", {
2512
+ enumerable: true,
2513
+ get: function() {
2514
+ return BINARY_AGGREGATES;
2515
+ }
2516
+ });
2393
2517
  Object.defineProperty(exports, "COMPOSED_DEVICE_STABLE_ID_PREFIX", {
2394
2518
  enumerable: true,
2395
2519
  get: function() {
@@ -2420,6 +2544,12 @@ Object.defineProperty(exports, "CUSTOMIZATION_BLOCK_NAME_PREFIX", {
2420
2544
  return CUSTOMIZATION_BLOCK_NAME_PREFIX;
2421
2545
  }
2422
2546
  });
2547
+ Object.defineProperty(exports, "CompositionAggregateSourceSchema", {
2548
+ enumerable: true,
2549
+ get: function() {
2550
+ return CompositionAggregateSourceSchema;
2551
+ }
2552
+ });
2423
2553
  Object.defineProperty(exports, "CompositionBindingSchema", {
2424
2554
  enumerable: true,
2425
2555
  get: function() {
@@ -2594,6 +2724,36 @@ Object.defineProperty(exports, "Fmp4BoxSplitter", {
2594
2724
  return Fmp4BoxSplitter;
2595
2725
  }
2596
2726
  });
2727
+ Object.defineProperty(exports, "GROUP_TEMPLATE_IDS", {
2728
+ enumerable: true,
2729
+ get: function() {
2730
+ return GROUP_TEMPLATE_IDS;
2731
+ }
2732
+ });
2733
+ Object.defineProperty(exports, "GroupHelperSpecSchema", {
2734
+ enumerable: true,
2735
+ get: function() {
2736
+ return GroupHelperSpecSchema;
2737
+ }
2738
+ });
2739
+ Object.defineProperty(exports, "GroupTemplateIdSchema", {
2740
+ enumerable: true,
2741
+ get: function() {
2742
+ return GroupTemplateIdSchema;
2743
+ }
2744
+ });
2745
+ Object.defineProperty(exports, "HelperMemberSchema", {
2746
+ enumerable: true,
2747
+ get: function() {
2748
+ return HelperMemberSchema;
2749
+ }
2750
+ });
2751
+ Object.defineProperty(exports, "HelperSpecSchema", {
2752
+ enumerable: true,
2753
+ get: function() {
2754
+ return HelperSpecSchema;
2755
+ }
2756
+ });
2597
2757
  Object.defineProperty(exports, "MAX_COMPOSITION_CODE_LENGTH", {
2598
2758
  enumerable: true,
2599
2759
  get: function() {
@@ -2648,6 +2808,18 @@ Object.defineProperty(exports, "MAX_EXPRESSION_SOURCE_LENGTH", {
2648
2808
  return MAX_EXPRESSION_SOURCE_LENGTH;
2649
2809
  }
2650
2810
  });
2811
+ Object.defineProperty(exports, "MAX_HELPER_MEMBERS", {
2812
+ enumerable: true,
2813
+ get: function() {
2814
+ return MAX_HELPER_MEMBERS;
2815
+ }
2816
+ });
2817
+ Object.defineProperty(exports, "NUMERIC_AGGREGATES", {
2818
+ enumerable: true,
2819
+ get: function() {
2820
+ return NUMERIC_AGGREGATES;
2821
+ }
2822
+ });
2651
2823
  Object.defineProperty(exports, "RESERVED_BINDING_NAMES", {
2652
2824
  enumerable: true,
2653
2825
  get: function() {
@@ -1,5 +1,5 @@
1
1
  import type { CapVocabularySlice } from '../types.js';
2
- type SensorCap = 'air-quality-sensor' | 'ambient-light-sensor' | 'binary' | 'carbon-monoxide' | 'connectivity' | 'contact' | 'enum-sensor' | 'flood' | 'gas' | 'humidity-sensor' | 'motion' | 'numeric-sensor' | 'power-meter' | 'presence' | 'pressure-sensor' | 'smoke' | 'tamper' | 'temperature-sensor' | 'vibration' | 'weather' | 'battery' | 'network-link' | 'device-status' | 'doorbell' | 'event-emitter';
2
+ type SensorCap = 'air-quality-sensor' | 'ambient-light-sensor' | 'binary' | 'carbon-monoxide' | 'connectivity' | 'contact' | 'enum-sensor' | 'flood' | 'gas' | 'group-status' | 'humidity-sensor' | 'motion' | 'numeric-sensor' | 'power-meter' | 'presence' | 'pressure-sensor' | 'smoke' | 'tamper' | 'temperature-sensor' | 'vibration' | 'weather' | 'battery' | 'network-link' | 'device-status' | 'doorbell' | 'event-emitter';
3
3
  /** The sensor family of the vocabulary. */
4
4
  export declare const SENSOR_CAP_VOCABULARY: CapVocabularySlice<SensorCap>;
5
5
  export {};
@@ -179,9 +179,9 @@ export interface NumericStateTrigger<C extends VocabCapName> {
179
179
  /** The quantity WITH its article, as it reads in a sentence ("the temperature" / "la temperatura"). */
180
180
  readonly quantity: L10n;
181
181
  readonly label: L10n;
182
- /** Display unit; omit when the device states it (see `unitField`). */
182
+ /** Display unit. The stored number is the quantity base; this is the label. */
183
183
  readonly unit?: string;
184
- /** A status field carrying the device's own unit (`numeric-sensor.unit`). */
184
+ /** @deprecated D944 removed slice unit siblings. Kept so old rows still parse. */
185
185
  readonly unitField?: CapStatusField<C>;
186
186
  readonly icon: VocabIcon;
187
187
  }
@@ -1,8 +1,11 @@
1
1
  import { z } from 'zod';
2
+ import { type ReadingQuantity } from '../units/quantity.js';
2
3
  export interface WireableField {
3
4
  readonly path: string;
4
5
  readonly kind: 'string' | 'number' | 'boolean' | 'enum';
5
6
  readonly enumValues?: readonly string[];
7
+ /** Quantity of a numeric reading, when the schema declares one. */
8
+ readonly quantity?: ReadingQuantity;
6
9
  /** True when `path` is relative to ONE item of an item-array cap (see
7
10
  * `CapabilityStatusItemArray`) — a link targeting it must carry a
8
11
  * `target.itemKey`. Absent/false for plain status fields. */
@@ -17,12 +17,12 @@ import { z } from 'zod';
17
17
  * providers detect renames by matching `uniqueId` and patch `id`
18
18
  * in-place so dispatch keeps working.
19
19
  *
20
- * **Rendering metadata is NOT stored here.** Display fields (`unit`,
21
- * `precision`) live in the cap STATUS SLICE so they flow live with every
22
- * state push and are the single source of truth. The `device_class` tag is
23
- * an HA-specific input that is consumed at the discovery boundary
24
- * (`ha-domain-mapping`) to derive the CamStack `DeviceRole`; it is never
25
- * stored after that point.
20
+ * **Display unit and precision are NOT stored here.** They are a read-time
21
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
22
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
23
+ * neither is repeated on each sample. The `device_class` tag is an
24
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
25
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
26
26
  *
27
27
  * The `raw` escape hatch carries provider-specific attributes that don't
28
28
  * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
@@ -32,6 +32,12 @@ export declare const SourceInfoSchema: z.ZodObject<{
32
32
  id: z.ZodString;
33
33
  system: z.ZodString;
34
34
  uniqueId: z.ZodOptional<z.ZodString>;
35
+ quantity: z.ZodOptional<z.ZodType<"angle" | "apparent-power" | "area" | "bitrate" | "colour-temperature" | "concentration-mass" | "concentration-ppb" | "concentration-volume" | "count" | "current" | "device" | "duration" | "duration-ms" | "energy" | "humidity" | "illuminance" | "index" | "information" | "irradiance" | "length" | "mass" | "none" | "percentage" | "power" | "precipitation" | "precipitation-rate" | "pressure" | "rate" | "ratio" | "signal-level" | "sound-level" | "speed" | "temperature" | "voltage" | "volume", unknown, z.core.$ZodTypeInternals<"angle" | "apparent-power" | "area" | "bitrate" | "colour-temperature" | "concentration-mass" | "concentration-ppb" | "concentration-volume" | "count" | "current" | "device" | "duration" | "duration-ms" | "energy" | "humidity" | "illuminance" | "index" | "information" | "irradiance" | "length" | "mass" | "none" | "percentage" | "power" | "precipitation" | "precipitation-rate" | "pressure" | "rate" | "ratio" | "signal-level" | "sound-level" | "speed" | "temperature" | "voltage" | "volume", unknown>>>;
36
+ dateTimeShape: z.ZodOptional<z.ZodEnum<{
37
+ date: "date";
38
+ datetime: "datetime";
39
+ time: "time";
40
+ }>>;
35
41
  raw: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
36
42
  }, z.core.$strip>;
37
43
  export type SourceInfo = z.infer<typeof SourceInfoSchema>;
@@ -3843,6 +3843,20 @@ export type AppRouter = TrpcCoreRouter<{
3843
3843
  meta: object;
3844
3844
  }>;
3845
3845
  }>>;
3846
+ groupStatus: TRPCBuiltRouter<{
3847
+ ctx: TrpcContext;
3848
+ meta: object;
3849
+ errorShape: AugmentedErrorShape;
3850
+ transformer: true;
3851
+ }, TRPCDecorateCreateRouterOptions<{
3852
+ getStatus: TRPCQueryProcedure<{
3853
+ input: {
3854
+ [x: string]: unknown;
3855
+ } & z.input<typeof DEVICE_STATUS_METHOD.getStatus.input>;
3856
+ output: z.infer<typeof DEVICE_STATUS_METHOD.getStatus.output>;
3857
+ meta: object;
3858
+ }>;
3859
+ }>>;
3846
3860
  heatingValve: TRPCBuiltRouter<{
3847
3861
  ctx: TrpcContext;
3848
3862
  meta: object;