@camstack/types 1.1.52 → 1.1.54

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.
@@ -578,7 +578,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
578
578
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
579
579
  resolve: import("@trpc/server").TRPCQueryProcedure<{
580
580
  input: {
581
- prefer?: "none" | "videotoolbox" | "cuda" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | null | undefined;
581
+ prefer?: "none" | "cuda" | "videotoolbox" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | null | undefined;
582
582
  } | undefined;
583
583
  output: {
584
584
  preferred: readonly string[];
@@ -589,7 +589,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
589
589
  resolveForNode: import("@trpc/server").TRPCQueryProcedure<{
590
590
  input: {
591
591
  nodeId: string;
592
- prefer?: "none" | "videotoolbox" | "cuda" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | null | undefined;
592
+ prefer?: "none" | "cuda" | "videotoolbox" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | null | undefined;
593
593
  };
594
594
  output: {
595
595
  preferred: readonly string[];
@@ -3410,6 +3410,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
3410
3410
  inputChannels?: number | undefined;
3411
3411
  inputLayout?: "nchw" | "nhwc" | undefined;
3412
3412
  inputNormalization?: "none" | "zero-one" | "imagenet" | undefined;
3413
+ outputProbabilities?: boolean | undefined;
3413
3414
  preprocessMode?: "letterbox" | "resize" | undefined;
3414
3415
  postprocessor?: import("@camstack/types").PostprocessorType | undefined;
3415
3416
  faceAlignment?: boolean | undefined;
@@ -8257,6 +8258,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
8257
8258
  inputChannels?: number | undefined;
8258
8259
  inputLayout?: "nchw" | "nhwc" | undefined;
8259
8260
  inputNormalization?: "none" | "zero-one" | "imagenet" | undefined;
8261
+ outputProbabilities?: boolean | undefined;
8260
8262
  preprocessMode?: "letterbox" | "resize" | undefined;
8261
8263
  postprocessor?: import("@camstack/types").PostprocessorType | undefined;
8262
8264
  faceAlignment?: boolean | undefined;
@@ -8364,6 +8366,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
8364
8366
  inputChannels?: number | undefined;
8365
8367
  inputLayout?: "nchw" | "nhwc" | undefined;
8366
8368
  inputNormalization?: "none" | "zero-one" | "imagenet" | undefined;
8369
+ outputProbabilities?: boolean | undefined;
8367
8370
  preprocessMode?: "letterbox" | "resize" | undefined;
8368
8371
  postprocessor?: import("@camstack/types").PostprocessorType | undefined;
8369
8372
  faceAlignment?: boolean | undefined;
@@ -10646,6 +10649,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
10646
10649
  } | undefined;
10647
10650
  deviceId?: number | undefined;
10648
10651
  sessionId?: string | undefined;
10652
+ frameId?: number | undefined;
10649
10653
  };
10650
10654
  output: {
10651
10655
  results: readonly import("@camstack/types").FrameResult[];
@@ -12187,7 +12191,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
12187
12191
  scores: readonly {
12188
12192
  runtime: "node" | "python";
12189
12193
  backend: string;
12190
- format: "onnx" | "coreml" | "openvino";
12194
+ format: "onnx" | "coreml" | "openvino" | "tflite";
12191
12195
  score: number;
12192
12196
  reason: string;
12193
12197
  available: boolean;
@@ -12195,12 +12199,21 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
12195
12199
  bestScore: {
12196
12200
  runtime: "node" | "python";
12197
12201
  backend: string;
12198
- format: "onnx" | "coreml" | "openvino";
12202
+ format: "onnx" | "coreml" | "openvino" | "tflite";
12199
12203
  score: number;
12200
12204
  reason: string;
12201
12205
  available: boolean;
12202
12206
  };
12203
12207
  pythonPath: string | null;
12208
+ devices: readonly {
12209
+ key: string;
12210
+ backend: string;
12211
+ device: string;
12212
+ format: "onnx" | "coreml" | "openvino" | "tflite" | "pt" | "gguf";
12213
+ runtime: "python";
12214
+ score: number;
12215
+ available: boolean;
12216
+ }[];
12204
12217
  };
12205
12218
  meta: object;
12206
12219
  }>;
@@ -12253,7 +12266,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
12253
12266
  resolveHwAccel: import("@trpc/server").TRPCQueryProcedure<{
12254
12267
  input: {
12255
12268
  [x: string]: unknown;
12256
- prefer?: "none" | "coreml" | "openvino" | "tensorrt" | "videotoolbox" | "cuda" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | "directml" | "webgpu" | null | undefined;
12269
+ prefer?: "none" | "coreml" | "openvino" | "tensorrt" | "cuda" | "videotoolbox" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | "directml" | "webgpu" | null | undefined;
12257
12270
  };
12258
12271
  output: {
12259
12272
  preferred: readonly string[];
@@ -17309,7 +17322,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
17309
17322
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
17310
17323
  resolve: import("@trpc/server").TRPCQueryProcedure<{
17311
17324
  input: {
17312
- prefer?: "none" | "videotoolbox" | "cuda" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | null | undefined;
17325
+ prefer?: "none" | "cuda" | "videotoolbox" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | null | undefined;
17313
17326
  } | undefined;
17314
17327
  output: {
17315
17328
  preferred: readonly string[];
@@ -17320,7 +17333,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
17320
17333
  resolveForNode: import("@trpc/server").TRPCQueryProcedure<{
17321
17334
  input: {
17322
17335
  nodeId: string;
17323
- prefer?: "none" | "videotoolbox" | "cuda" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | null | undefined;
17336
+ prefer?: "none" | "cuda" | "videotoolbox" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | null | undefined;
17324
17337
  };
17325
17338
  output: {
17326
17339
  preferred: readonly string[];
@@ -20141,6 +20154,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
20141
20154
  inputChannels?: number | undefined;
20142
20155
  inputLayout?: "nchw" | "nhwc" | undefined;
20143
20156
  inputNormalization?: "none" | "zero-one" | "imagenet" | undefined;
20157
+ outputProbabilities?: boolean | undefined;
20144
20158
  preprocessMode?: "letterbox" | "resize" | undefined;
20145
20159
  postprocessor?: import("@camstack/types").PostprocessorType | undefined;
20146
20160
  faceAlignment?: boolean | undefined;
@@ -24988,6 +25002,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
24988
25002
  inputChannels?: number | undefined;
24989
25003
  inputLayout?: "nchw" | "nhwc" | undefined;
24990
25004
  inputNormalization?: "none" | "zero-one" | "imagenet" | undefined;
25005
+ outputProbabilities?: boolean | undefined;
24991
25006
  preprocessMode?: "letterbox" | "resize" | undefined;
24992
25007
  postprocessor?: import("@camstack/types").PostprocessorType | undefined;
24993
25008
  faceAlignment?: boolean | undefined;
@@ -25095,6 +25110,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
25095
25110
  inputChannels?: number | undefined;
25096
25111
  inputLayout?: "nchw" | "nhwc" | undefined;
25097
25112
  inputNormalization?: "none" | "zero-one" | "imagenet" | undefined;
25113
+ outputProbabilities?: boolean | undefined;
25098
25114
  preprocessMode?: "letterbox" | "resize" | undefined;
25099
25115
  postprocessor?: import("@camstack/types").PostprocessorType | undefined;
25100
25116
  faceAlignment?: boolean | undefined;
@@ -27377,6 +27393,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
27377
27393
  } | undefined;
27378
27394
  deviceId?: number | undefined;
27379
27395
  sessionId?: string | undefined;
27396
+ frameId?: number | undefined;
27380
27397
  };
27381
27398
  output: {
27382
27399
  results: readonly import("@camstack/types").FrameResult[];
@@ -28918,7 +28935,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
28918
28935
  scores: readonly {
28919
28936
  runtime: "node" | "python";
28920
28937
  backend: string;
28921
- format: "onnx" | "coreml" | "openvino";
28938
+ format: "onnx" | "coreml" | "openvino" | "tflite";
28922
28939
  score: number;
28923
28940
  reason: string;
28924
28941
  available: boolean;
@@ -28926,12 +28943,21 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
28926
28943
  bestScore: {
28927
28944
  runtime: "node" | "python";
28928
28945
  backend: string;
28929
- format: "onnx" | "coreml" | "openvino";
28946
+ format: "onnx" | "coreml" | "openvino" | "tflite";
28930
28947
  score: number;
28931
28948
  reason: string;
28932
28949
  available: boolean;
28933
28950
  };
28934
28951
  pythonPath: string | null;
28952
+ devices: readonly {
28953
+ key: string;
28954
+ backend: string;
28955
+ device: string;
28956
+ format: "onnx" | "coreml" | "openvino" | "tflite" | "pt" | "gguf";
28957
+ runtime: "python";
28958
+ score: number;
28959
+ available: boolean;
28960
+ }[];
28935
28961
  };
28936
28962
  meta: object;
28937
28963
  }>;
@@ -28984,7 +29010,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
28984
29010
  resolveHwAccel: import("@trpc/server").TRPCQueryProcedure<{
28985
29011
  input: {
28986
29012
  [x: string]: unknown;
28987
- prefer?: "none" | "coreml" | "openvino" | "tensorrt" | "videotoolbox" | "cuda" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | "directml" | "webgpu" | null | undefined;
29013
+ prefer?: "none" | "coreml" | "openvino" | "tensorrt" | "cuda" | "videotoolbox" | "nvdec" | "vaapi" | "qsv" | "d3d11va" | "dxva2" | "amf" | "vdpau" | "drm" | "directml" | "webgpu" | null | undefined;
28988
29014
  };
28989
29015
  output: {
28990
29016
  preferred: readonly string[];
package/dist/index.d.ts CHANGED
@@ -160,6 +160,8 @@ export { getByPath, setByPath } from './device/path-util.js';
160
160
  export { applyTransform } from './device/device-link-transform.js';
161
161
  export { enumerateItemArrayFields, enumerateSchemaFields } from './device/schema-fields.js';
162
162
  export type { WireableField } from './device/schema-fields.js';
163
+ export { DEVICE_TYPE_CONTROL_KIND, resolveDeviceControlKind, MOTION_TRIGGER_FEATURE, hasMotionTrigger, SENSOR_FEATURES, SENSOR_MAP, pickAccessoryControl, isNode, resolveMutate, } from './device/device-control-resolution.js';
164
+ export type { DeviceControlKind, AccessoryControlKind, AccessoryControlPick, AccessoryControlInput, SensorMapping, } from './device/device-control-resolution.js';
163
165
  export { EventCategory } from './enums/event-category.js';
164
166
  export type { AppRouter, AddonApi } from './generated/addon-api.js';
165
167
  export { createDeviceProxy } from './generated/device-proxy.js';
@@ -190,5 +192,5 @@ export { RingBuffer } from './utils/ring-buffer.js';
190
192
  export { rectsToCells, cellsToRects, type NormRect } from './utils/privacy-grid-raster.js';
191
193
  export { sleep, sleepCancellable } from './utils/sleep.js';
192
194
  export { bindAddonActions } from './helpers/bind-addon-actions.js';
193
- export { supportedRuntimes, runtimeDevices, defaultDeviceFor, modelFormatForRuntime, scoreRuntimes, } from './inference/runtime-capabilities.js';
194
- export type { RuntimeId, DeviceOption } from './inference/runtime-capabilities.js';
195
+ export { supportedRuntimes, runtimeDevices, defaultDeviceFor, modelFormatForRuntime, scoreRuntimes, enumerateInferenceDevices, } from './inference/runtime-capabilities.js';
196
+ export type { RuntimeId, DeviceOption, InferenceDeviceDescriptor, } from './inference/runtime-capabilities.js';
package/dist/index.js CHANGED
@@ -174,6 +174,17 @@ var ModelCatalogEntrySchema = zod.z.object({
174
174
  "imagenet",
175
175
  "none"
176
176
  ]).optional(),
177
+ /**
178
+ * The model already applies softmax IN-GRAPH — its raw output is a
179
+ * probability distribution, not logits. When set, the `softmax`
180
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
181
+ * probability vector collapses it toward uniform (top-1 score craters far
182
+ * below its true value, making every confidence gate meaningless). Absent ⇒
183
+ * the output is raw logits and the postprocessor applies softmax (the normal
184
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
185
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
186
+ */
187
+ outputProbabilities: zod.z.boolean().optional(),
177
188
  preprocessMode: zod.z.enum(["letterbox", "resize"]).optional(),
178
189
  /**
179
190
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -8212,7 +8223,14 @@ var pipelineExecutorCapability = {
8212
8223
  * (inputClasses ≠ null) are skipped and served per-track via
8213
8224
  * pipelineRunner.runDetailSubtree (two-plane design).
8214
8225
  */
8215
- plane: zod.z.enum(["full", "frame"]).optional()
8226
+ plane: zod.z.enum(["full", "frame"]).optional(),
8227
+ /**
8228
+ * Inference-device selector (Phase 2 multi-device). Format
8229
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
8230
+ * Omitted ⇒ the runner's default device (current single-engine
8231
+ * behaviour). Selects WHICH device pool of the node runs the call.
8232
+ */
8233
+ deviceKey: zod.z.string().optional()
8216
8234
  }), PipelineRunResultBridge, { kind: "mutation" }),
8217
8235
  /**
8218
8236
  * Batched run — N raw frames packed into one cap call. The provider
@@ -8230,7 +8248,20 @@ var pipelineExecutorCapability = {
8230
8248
  steps: zod.z.array(PipelineStepInputSchema).min(1),
8231
8249
  frames: zod.z.array(FrameInputSchema).min(1).max(255),
8232
8250
  deviceId: zod.z.number().optional(),
8233
- sessionId: zod.z.string().optional()
8251
+ sessionId: zod.z.string().optional(),
8252
+ /**
8253
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
8254
+ * the batch to the Python pool's bench preprocess cache
8255
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
8256
+ * preprocessed ONCE and every later inference is a pure-inference cache
8257
+ * hit — the sustained-throughput run measures inference, not
8258
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
8259
+ * full preprocess every call, correct). Fresh per sustained run;
8260
+ * released via `uncacheFrame`.
8261
+ */
8262
+ frameId: zod.z.number().int().nonnegative().optional(),
8263
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
8264
+ deviceKey: zod.z.string().optional()
8234
8265
  }), zod.z.object({ results: zod.z.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
8235
8266
  /**
8236
8267
  * Cache a raw frame inside the Python inference pool's memory.
@@ -8684,7 +8715,14 @@ var RunnerCameraConfigSchema = zod.z.object({
8684
8715
  * camera's detect node differs from its source-owner (P2d, gated by the
8685
8716
  * `remoteSourcingNodes` rollout setting).
8686
8717
  */
8687
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
8718
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
8719
+ /**
8720
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
8721
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
8722
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
8723
+ * this only selects WHICH device pool of that node runs the session.
8724
+ */
8725
+ deviceKey: zod.z.string().optional()
8688
8726
  });
8689
8727
  /**
8690
8728
  * Per-device settings UI fields the runner cap owns. Co-located with
@@ -12788,6 +12826,182 @@ function enumerateItemArrayFields(itemArray) {
12788
12826
  }));
12789
12827
  }
12790
12828
  //#endregion
12829
+ //#region src/device/device-control-resolution.ts
12830
+ /**
12831
+ * Device-control RESOLUTION LOGIC — the single, presentation-free source of
12832
+ * truth for "given a device's `type` (+ its cap `features`), which control
12833
+ * should render?".
12834
+ *
12835
+ * This module is deliberately framework-free: NO react / react-native / lucide
12836
+ * imports, no JSX, no platform widgets. It encodes only the DECISION; each
12837
+ * client (admin-ui's `@camstack/ui-library` web registry, the RN viewer's
12838
+ * accessory registry) maps the returned control-KIND onto its own
12839
+ * platform-specific components. That's the one duplication we remove — the
12840
+ * presentational components stay platform-specific by necessity.
12841
+ *
12842
+ * Two decision surfaces live here, at two granularities:
12843
+ *
12844
+ * 1. `resolveDeviceControlKind(type)` — the BASE type → control-kind decision
12845
+ * used by the admin-ui device list / device-detail hero. Exhaustive over
12846
+ * every `DeviceType`; a light is a single `'light'` kind (dim-vs-plain is
12847
+ * refined inside the light component).
12848
+ *
12849
+ * 2. `pickAccessoryControl({ type, features })` — the FINER accessory-row
12850
+ * decision used by the viewer's per-camera accessory list. It refines a
12851
+ * `light` into `'brightness'` vs `'switch'` and a `sensor`/`presence` into
12852
+ * a specific sensor cap, because an accessory row renders ONE compact
12853
+ * control chosen by priority.
12854
+ *
12855
+ * The two agree on every overlapping type EXCEPT `light` (see the notes on
12856
+ * `pickAccessoryControl`): the base decision keeps a dedicated `'light'`
12857
+ * control; the accessory decision collapses a non-dimmable light onto the
12858
+ * generic `'switch'` control. That is a deliberate granularity difference, not
12859
+ * a contradiction — the base kind is what the admin-ui renders, the accessory
12860
+ * pick is what a compact camera-accessory row renders.
12861
+ */
12862
+ /**
12863
+ * Exhaustive `DeviceType` → `DeviceControlKind` map. Omitting a new enum member
12864
+ * fails the build here (the single place the type→kind decision is encoded).
12865
+ * `'dummy'` marks types with no interactive control (Camera/Hub/Generic and the
12866
+ * placeholder HA types whose inline control renders a dash).
12867
+ */
12868
+ var DEVICE_TYPE_CONTROL_KIND = {
12869
+ [require_sleep.DeviceType.Cover]: "cover",
12870
+ [require_sleep.DeviceType.Valve]: "valve",
12871
+ [require_sleep.DeviceType.Humidifier]: "humidifier",
12872
+ [require_sleep.DeviceType.WaterHeater]: "water-heater",
12873
+ [require_sleep.DeviceType.Camera]: "dummy",
12874
+ [require_sleep.DeviceType.Hub]: "dummy",
12875
+ [require_sleep.DeviceType.Switch]: "switch",
12876
+ [require_sleep.DeviceType.Siren]: "switch",
12877
+ [require_sleep.DeviceType.Light]: "light",
12878
+ [require_sleep.DeviceType.Fan]: "fan",
12879
+ [require_sleep.DeviceType.Sensor]: "sensor",
12880
+ [require_sleep.DeviceType.Thermostat]: "thermostat",
12881
+ [require_sleep.DeviceType.Climate]: "climate",
12882
+ [require_sleep.DeviceType.Button]: "button",
12883
+ [require_sleep.DeviceType.EventEmitter]: "dummy",
12884
+ [require_sleep.DeviceType.Update]: "update",
12885
+ [require_sleep.DeviceType.Generic]: "dummy",
12886
+ [require_sleep.DeviceType.Notifier]: "dummy",
12887
+ [require_sleep.DeviceType.Script]: "dummy",
12888
+ [require_sleep.DeviceType.Automation]: "dummy",
12889
+ [require_sleep.DeviceType.Lock]: "lock",
12890
+ [require_sleep.DeviceType.MediaPlayer]: "media-player",
12891
+ [require_sleep.DeviceType.AlarmPanel]: "alarm",
12892
+ [require_sleep.DeviceType.Control]: "control",
12893
+ [require_sleep.DeviceType.Presence]: "sensor",
12894
+ [require_sleep.DeviceType.Weather]: "weather",
12895
+ [require_sleep.DeviceType.Vacuum]: "vacuum",
12896
+ [require_sleep.DeviceType.LawnMower]: "lawn-mower",
12897
+ [require_sleep.DeviceType.Container]: "dummy",
12898
+ [require_sleep.DeviceType.Image]: "image",
12899
+ [require_sleep.DeviceType.PetFeeder]: "pet-feeder"
12900
+ };
12901
+ var DEVICE_TYPE_VALUES = new Set(Object.values(require_sleep.DeviceType));
12902
+ /**
12903
+ * Runtime-string-safe base resolver: a device row's `type` crosses the wire as
12904
+ * a plain string, so an unrecognised value resolves to `null` instead of
12905
+ * indexing the map with an unverified key. `DEVICE_TYPE_VALUES.has(type)`
12906
+ * narrows the string to a `DeviceType` before indexing (documented enum-string
12907
+ * boundary).
12908
+ */
12909
+ function resolveDeviceControlKind(type) {
12910
+ return DEVICE_TYPE_VALUES.has(type) ? DEVICE_TYPE_CONTROL_KIND[type] : null;
12911
+ }
12912
+ /** Kebab feature flag advertised by an accessory that auto-activates when its
12913
+ * parent camera detects motion. The cap's kebab name is `motion-trigger` (see
12914
+ * `motion-trigger.cap.ts`). It is INDEPENDENT of the row's primary control: a
12915
+ * siren/light/switch can expose BOTH a `switch` on/off AND a `motion-trigger`
12916
+ * "activate on motion" toggle. */
12917
+ var MOTION_TRIGGER_FEATURE = "motion-trigger";
12918
+ /** True when an accessory advertises the `motion-trigger` cap — drives the
12919
+ * inline "On motion" toggle rendered alongside its primary control. Pure +
12920
+ * testable. */
12921
+ function hasMotionTrigger(features) {
12922
+ return features.includes(MOTION_TRIGGER_FEATURE);
12923
+ }
12924
+ /** Kebab-case sensor cap names → the read-only sensor-value control. The
12925
+ * accessory pick scans these (in order) to choose which sensor cap to read. */
12926
+ var SENSOR_FEATURES = [
12927
+ "temperature-sensor",
12928
+ "humidity-sensor",
12929
+ "ambient-light-sensor",
12930
+ "numeric-sensor",
12931
+ "enum-sensor"
12932
+ ];
12933
+ /**
12934
+ * Pick the primary control for an accessory. The control is chosen by the
12935
+ * device's canonical `type` (the `DeviceType` enum value — the same key
12936
+ * `resolveDeviceControlKind` maps on), NOT by the `features` list (which holds
12937
+ * secondary flags). For a `light` we consult `features` to choose dim vs plain
12938
+ * on/off, and for a `sensor`/`presence` to pick which sensor value to read.
12939
+ * Pure: same input → same output. Returns `{ kind: 'none' }` for types with no
12940
+ * accessory-row control.
12941
+ */
12942
+ function pickAccessoryControl(device) {
12943
+ const set = new Set(device.features);
12944
+ switch (device.type) {
12945
+ case require_sleep.DeviceType.Cover: return { kind: "cover" };
12946
+ case require_sleep.DeviceType.Valve: return { kind: "valve" };
12947
+ case require_sleep.DeviceType.Lock: return { kind: "lock" };
12948
+ case require_sleep.DeviceType.AlarmPanel: return { kind: "alarm" };
12949
+ case require_sleep.DeviceType.Light: return set.has("brightness") ? { kind: "brightness" } : { kind: "switch" };
12950
+ case require_sleep.DeviceType.Switch:
12951
+ case require_sleep.DeviceType.Siren: return { kind: "switch" };
12952
+ case require_sleep.DeviceType.Sensor:
12953
+ case require_sleep.DeviceType.Presence: {
12954
+ const sensor = SENSOR_FEATURES.find((f) => set.has(f));
12955
+ return sensor ? {
12956
+ kind: "sensor",
12957
+ sensorFeature: sensor
12958
+ } : { kind: "none" };
12959
+ }
12960
+ default: return { kind: "none" };
12961
+ }
12962
+ }
12963
+ /** Kebab sensor cap → its numeric-value mapping. `enum-sensor` is handled
12964
+ * separately by callers (it reads a raw string `value`, not a numeric field),
12965
+ * so it intentionally has no entry here. */
12966
+ var SENSOR_MAP = {
12967
+ "temperature-sensor": {
12968
+ capKey: "temperatureSensor",
12969
+ field: "celsius",
12970
+ fallbackUnit: "°C"
12971
+ },
12972
+ "humidity-sensor": {
12973
+ capKey: "humiditySensor",
12974
+ field: "percent",
12975
+ fallbackUnit: "%"
12976
+ },
12977
+ "ambient-light-sensor": {
12978
+ capKey: "ambientLightSensor",
12979
+ field: "lux",
12980
+ fallbackUnit: "lx"
12981
+ },
12982
+ "numeric-sensor": {
12983
+ capKey: "numericSensor",
12984
+ field: "value"
12985
+ }
12986
+ };
12987
+ /** True when a value is a navigable node: a plain object OR a tRPC v11 client
12988
+ * proxy node (a callable `Proxy(noop)` → `typeof === 'function'`, NOT an
12989
+ * object). A `typeof === 'object'`-only check REJECTS every real client proxy,
12990
+ * so `resolveMutate` would return null and EVERY control becomes a silent
12991
+ * no-op. Framework-free: operates on the SDK proxy shape only. */
12992
+ function isNode(value) {
12993
+ return value !== null && (typeof value === "object" || typeof value === "function");
12994
+ }
12995
+ /** Resolve `router.<method>.mutate` to a callable, or `null`. Walks the tRPC
12996
+ * client proxy defensively (each hop guarded by `isNode`). */
12997
+ function resolveMutate(router, method) {
12998
+ if (!isNode(router)) return null;
12999
+ const proc = router[method];
13000
+ if (!isNode(proc)) return null;
13001
+ const mutate = proc.mutate;
13002
+ return typeof mutate === "function" ? mutate : null;
13003
+ }
13004
+ //#endregion
12791
13005
  //#region src/utils/zone-rule-eval.ts
12792
13006
  /**
12793
13007
  * Evaluate `rules` against `items`. Returns the items partitioned
@@ -23553,17 +23767,28 @@ var PlatformScoreSchema = zod.z.object({
23553
23767
  format: zod.z.enum([
23554
23768
  "onnx",
23555
23769
  "coreml",
23556
- "openvino"
23770
+ "openvino",
23771
+ "tflite"
23557
23772
  ]),
23558
23773
  score: zod.z.number(),
23559
23774
  reason: zod.z.string(),
23560
23775
  available: zod.z.boolean()
23561
23776
  });
23777
+ var InferenceDeviceDescriptorSchema = zod.z.object({
23778
+ key: zod.z.string(),
23779
+ backend: zod.z.string(),
23780
+ device: zod.z.string(),
23781
+ format: ModelFormatSchema,
23782
+ runtime: zod.z.literal("python"),
23783
+ score: zod.z.number(),
23784
+ available: zod.z.boolean()
23785
+ });
23562
23786
  var PlatformCapabilitiesSchema = zod.z.object({
23563
23787
  hardware: HardwareInfoSchema,
23564
23788
  scores: zod.z.array(PlatformScoreSchema).readonly(),
23565
23789
  bestScore: PlatformScoreSchema,
23566
- pythonPath: zod.z.string().nullable()
23790
+ pythonPath: zod.z.string().nullable(),
23791
+ devices: zod.z.array(InferenceDeviceDescriptorSchema).readonly()
23567
23792
  });
23568
23793
  var ModelRequirementSchema = zod.z.object({
23569
23794
  modelId: zod.z.string(),
@@ -31220,6 +31445,7 @@ function modelFormatForRuntime(id) {
31220
31445
  * - coreml: score 95, darwin+arm64
31221
31446
  * - cuda: score 85, nvidia gpu
31222
31447
  * - openvino: score 90 if intel-npu present, else 80 if intel gpu
31448
+ * - edgetpu: score 70, coral usb present
31223
31449
  * - cpu: score 50 (universal floor, always included)
31224
31450
  *
31225
31451
  * All hardware-matched entries are `available: true`.
@@ -31255,6 +31481,14 @@ function scoreRuntimes(hw) {
31255
31481
  available: true
31256
31482
  });
31257
31483
  }
31484
+ if (hw.coral != null) scores.push({
31485
+ runtime: "python",
31486
+ backend: "edgetpu",
31487
+ format: "tflite",
31488
+ score: 70,
31489
+ reason: "Coral Edge TPU (Python LiteRT)",
31490
+ available: true
31491
+ });
31258
31492
  scores.push({
31259
31493
  runtime: "python",
31260
31494
  backend: "cpu",
@@ -31269,6 +31503,33 @@ function scoreRuntimes(hw) {
31269
31503
  best: sorted.find((s) => s.available) ?? sorted[sorted.length - 1]
31270
31504
  };
31271
31505
  }
31506
+ var DEVICE_FOR_BACKEND = {
31507
+ openvino: "gpu",
31508
+ cuda: "gpu",
31509
+ edgetpu: "usb",
31510
+ coreml: "all",
31511
+ cpu: "cpu"
31512
+ };
31513
+ /**
31514
+ * Enumerate every usable inference device on a node, one descriptor per scored
31515
+ * backend. Unlike the elected `bestScore`, this returns the FULL set so the
31516
+ * orchestrator can run and balance across all accelerators concurrently.
31517
+ */
31518
+ function enumerateInferenceDevices(hw) {
31519
+ const { scores } = scoreRuntimes(hw);
31520
+ return scores.map((s) => {
31521
+ const device = DEVICE_FOR_BACKEND[s.backend] ?? s.backend;
31522
+ return {
31523
+ key: s.backend === "cpu" ? "cpu" : `${s.backend}:${device}`,
31524
+ backend: s.backend,
31525
+ device,
31526
+ format: s.format,
31527
+ runtime: "python",
31528
+ score: s.score,
31529
+ available: s.available
31530
+ };
31531
+ });
31532
+ }
31272
31533
  //#endregion
31273
31534
  exports.ACCESSORY_LABEL = ACCESSORY_LABEL;
31274
31535
  exports.ALL_CAPABILITY_DEFINITIONS = ALL_CAPABILITY_DEFINITIONS;
@@ -31441,6 +31702,7 @@ exports.DEVICE_CAP_NAMES = DEVICE_CAP_NAMES;
31441
31702
  exports.DEVICE_PROFILES = DEVICE_PROFILES;
31442
31703
  exports.DEVICE_SETTINGS_CONTRIBUTION_METHODS = require_sleep.DEVICE_SETTINGS_CONTRIBUTION_METHODS;
31443
31704
  exports.DEVICE_STATUS_METHOD = require_sleep.DEVICE_STATUS_METHOD;
31705
+ exports.DEVICE_TYPE_CONTROL_KIND = DEVICE_TYPE_CONTROL_KIND;
31444
31706
  exports.DEVICE_TYPE_INFO = DEVICE_TYPE_INFO;
31445
31707
  exports.DayNightModeSchema = DayNightModeSchema;
31446
31708
  exports.DayNightOptionsSchema = DayNightOptionsSchema;
@@ -31583,6 +31845,7 @@ exports.MAX_EXPRESSION_EVAL_STEPS = MAX_EXPRESSION_EVAL_STEPS;
31583
31845
  exports.MAX_EXPRESSION_SOURCE_LENGTH = MAX_EXPRESSION_SOURCE_LENGTH;
31584
31846
  exports.METHOD_ACCESS_MAP = METHOD_ACCESS_MAP;
31585
31847
  exports.MODEL_FORMATS = MODEL_FORMATS;
31848
+ exports.MOTION_TRIGGER_FEATURE = MOTION_TRIGGER_FEATURE;
31586
31849
  exports.ManagedModelCatalogEntrySchema = ManagedModelCatalogEntrySchema;
31587
31850
  exports.ManagedModelRefSchema = ManagedModelRefSchema;
31588
31851
  exports.ManagedRuntimeConfigSchema = ManagedRuntimeConfigSchema;
@@ -31748,6 +32011,8 @@ exports.SCOPE_PRESETS = SCOPE_PRESETS;
31748
32011
  exports.SCRUB_THUMBNAIL_PRESETS = SCRUB_THUMBNAIL_PRESETS;
31749
32012
  exports.SCRUB_THUMBNAIL_PRESET_LABELS = SCRUB_THUMBNAIL_PRESET_LABELS;
31750
32013
  exports.SCRUB_THUMBNAIL_PRESET_ORDER = SCRUB_THUMBNAIL_PRESET_ORDER;
32014
+ exports.SENSOR_FEATURES = SENSOR_FEATURES;
32015
+ exports.SENSOR_MAP = SENSOR_MAP;
31751
32016
  exports.SOURCE_INFO_METADATA_KEY = SOURCE_INFO_METADATA_KEY;
31752
32017
  exports.STREAM_PROFILE_META = STREAM_PROFILE_META;
31753
32018
  exports.STREAM_QUALITY_LABELS = STREAM_QUALITY_LABELS;
@@ -31974,6 +32239,7 @@ exports.emitDownForOwnedCaps = require_sleep.emitDownForOwnedCaps;
31974
32239
  exports.emitReadiness = require_sleep.emitReadiness;
31975
32240
  exports.encodeProfileFromStreamShape = encodeProfileFromStreamShape;
31976
32241
  exports.enumSensorCapability = enumSensorCapability;
32242
+ exports.enumerateInferenceDevices = enumerateInferenceDevices;
31977
32243
  exports.enumerateItemArrayFields = enumerateItemArrayFields;
31978
32244
  exports.enumerateSchemaFields = enumerateSchemaFields;
31979
32245
  exports.errMsg = require_err_msg.errMsg;
@@ -31999,6 +32265,7 @@ exports.getAudioMacroClassIds = getAudioMacroClassIds;
31999
32265
  exports.getByPath = getByPath;
32000
32266
  exports.getCapsByProviderKind = getCapsByProviderKind;
32001
32267
  exports.getTaxonomyEntry = getTaxonomyEntry;
32268
+ exports.hasMotionTrigger = hasMotionTrigger;
32002
32269
  exports.hfModelUrl = hfModelUrl;
32003
32270
  exports.htmlToText = htmlToText;
32004
32271
  exports.humidifierCapability = humidifierCapability;
@@ -32014,6 +32281,7 @@ exports.isCollectionArrayMethod = isCollectionArrayMethod;
32014
32281
  exports.isDeployableToAgent = isDeployableToAgent;
32015
32282
  exports.isDeviceConfigCap = require_sleep.isDeviceConfigCap;
32016
32283
  exports.isEvent = require_sleep.isEvent;
32284
+ exports.isNode = isNode;
32017
32285
  exports.isObjectInput = isObjectInput;
32018
32286
  exports.isVoidInput = isVoidInput;
32019
32287
  exports.jobKindSchema = jobKindSchema;
@@ -32072,6 +32340,7 @@ exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
32072
32340
  exports.parseProfileBrokerId = require_sleep.parseProfileBrokerId;
32073
32341
  exports.parseStreamParamsFormPatch = parseStreamParamsFormPatch;
32074
32342
  exports.petFeederCapability = petFeederCapability;
32343
+ exports.pickAccessoryControl = pickAccessoryControl;
32075
32344
  exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
32076
32345
  exports.pipelineAnalyticsCapability = pipelineAnalyticsCapability;
32077
32346
  exports.pipelineExecutorCapability = pipelineExecutorCapability;
@@ -32101,10 +32370,12 @@ exports.resolveAddonPlacement = resolveAddonPlacement;
32101
32370
  exports.resolveAddonRuntime = resolveAddonRuntime;
32102
32371
  exports.resolveCapMount = require_sleep.resolveCapMount;
32103
32372
  exports.resolveDetectionRuntime = resolveDetectionRuntime;
32373
+ exports.resolveDeviceControlKind = resolveDeviceControlKind;
32104
32374
  exports.resolveDeviceProfile = resolveDeviceProfile;
32105
32375
  exports.resolveFormat = resolveFormat;
32106
32376
  exports.resolveHydratedFieldValue = require_sleep.resolveHydratedFieldValue;
32107
32377
  exports.resolveModelFormat = resolveModelFormat;
32378
+ exports.resolveMutate = resolveMutate;
32108
32379
  exports.resolveRunnerId = resolveRunnerId;
32109
32380
  exports.resolveScrubThumbnailGeometry = resolveScrubThumbnailGeometry;
32110
32381
  exports.resolveVariantModelId = resolveVariantModelId;