@camstack/types 1.2.440 → 1.2.442

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 (38) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/addon.js +9 -3
  3. package/dist/addon.mjs +9 -3
  4. package/dist/capabilities/customize-catalog.d.ts +3 -0
  5. package/dist/capabilities/customize-spec.d.ts +20 -2
  6. package/dist/capabilities/index.d.ts +6 -2
  7. package/dist/capabilities/media-player.cap.d.ts +50 -0
  8. package/dist/capabilities/pet-feeder.cap.d.ts +9 -0
  9. package/dist/capabilities/remote.cap.d.ts +264 -0
  10. package/dist/capabilities/template.cap.d.ts +161 -0
  11. package/dist/client.js +14 -0
  12. package/dist/client.mjs +14 -0
  13. package/dist/{composition-DCmtFE-X.js → composition-C0dlyG9U.js} +17 -0
  14. package/dist/{composition-DpFv_J6D.mjs → composition-bIBW5_13.mjs} +12 -1
  15. package/dist/ffmpeg/invocation.d.ts +9 -0
  16. package/dist/generated/addon-api.d.ts +86 -0
  17. package/dist/generated/addon-cap-facts.d.ts +1 -1
  18. package/dist/generated/cap-input-defaults.d.ts +1 -1
  19. package/dist/generated/capability-router-map.d.ts +10 -4
  20. package/dist/generated/collection-array-methods.d.ts +1 -1
  21. package/dist/generated/customize-catalog.d.ts +18 -1
  22. package/dist/generated/method-access-map.d.ts +1 -1
  23. package/dist/generated/method-device-selectors.d.ts +2 -2
  24. package/dist/generated/system-proxy.d.ts +4 -0
  25. package/dist/index.d.ts +5 -0
  26. package/dist/index.js +863 -72
  27. package/dist/index.mjs +815 -73
  28. package/dist/interfaces/device-call-redirect.d.ts +42 -0
  29. package/dist/interfaces/kernel-abstractions.d.ts +12 -0
  30. package/dist/node.js +1 -1
  31. package/dist/node.mjs +1 -1
  32. package/dist/remote/remote-config.d.ts +116 -0
  33. package/dist/remote/remote-roles.d.ts +21 -0
  34. package/dist/remote/smartir-draft.d.ts +25 -0
  35. package/dist/{sleep-g6ZjBHBL.js → sleep-BpwNJ1Su.js} +19 -0
  36. package/dist/{sleep-BAvUCi_2.mjs → sleep-DJws6ho7.mjs} +19 -0
  37. package/dist/template/template-kinds.d.ts +26 -0
  38. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-g6ZjBHBL.js");
2
+ const require_sleep = require("./sleep-BpwNJ1Su.js");
3
3
  const require_event_category = require("./event-category-E17zaHjN.js");
4
- const require_composition = require("./composition-DCmtFE-X.js");
4
+ const require_composition = require("./composition-C0dlyG9U.js");
5
5
  const require_node_color = require("./node-color.js");
6
6
  const require_err_msg = require("./err-msg-COHgq7Mb.js");
7
7
  const require_enums = require("./enums.js");
@@ -65199,6 +65199,456 @@ onBrightnessChanged: { data: zod.z.object({
65199
65199
  } } }
65200
65200
  };
65201
65201
  //#endregion
65202
+ //#region src/remote/remote-roles.ts
65203
+ /**
65204
+ * The role vocabulary of a remote (D963): what a button MEANS, independent of
65205
+ * how it is wired. A layout template places buttons by role; a device binds a
65206
+ * role to an action; an exporter asks for a role by name. Roles are shared so
65207
+ * one layout fits every appliance and one voice command fits every binding.
65208
+ *
65209
+ * A role outside this list is `custom:<key>` — the operator's own button (a
65210
+ * SmartIR source, a channel preset). The key is a lowercase slug.
65211
+ */
65212
+ var REMOTE_KNOWN_ROLES = [
65213
+ "power",
65214
+ "power_on",
65215
+ "power_off",
65216
+ "volume_up",
65217
+ "volume_down",
65218
+ "mute",
65219
+ "channel_up",
65220
+ "channel_down",
65221
+ "up",
65222
+ "down",
65223
+ "left",
65224
+ "right",
65225
+ "ok",
65226
+ "back",
65227
+ "home",
65228
+ "menu",
65229
+ "info",
65230
+ "guide",
65231
+ "exit",
65232
+ "input",
65233
+ "play",
65234
+ "pause",
65235
+ "play_pause",
65236
+ "stop",
65237
+ "next",
65238
+ "previous",
65239
+ "fast_forward",
65240
+ "rewind",
65241
+ "digit_0",
65242
+ "digit_1",
65243
+ "digit_2",
65244
+ "digit_3",
65245
+ "digit_4",
65246
+ "digit_5",
65247
+ "digit_6",
65248
+ "digit_7",
65249
+ "digit_8",
65250
+ "digit_9"
65251
+ ];
65252
+ var REMOTE_CUSTOM_ROLE_PREFIX = "custom:";
65253
+ var CUSTOM_KEY_RE = /^[a-z0-9][a-z0-9_]{0,47}$/;
65254
+ var KNOWN_ROLES = new Set(REMOTE_KNOWN_ROLES);
65255
+ function isRemoteKnownRole(value) {
65256
+ return KNOWN_ROLES.has(value);
65257
+ }
65258
+ function isRemoteCustomKey(key) {
65259
+ return CUSTOM_KEY_RE.test(key);
65260
+ }
65261
+ function customRole(key) {
65262
+ return `${REMOTE_CUSTOM_ROLE_PREFIX}${key}`;
65263
+ }
65264
+ /** The key of a `custom:<key>` role, or null for any other string. */
65265
+ function customKeyOfRole(role) {
65266
+ if (!role.startsWith("custom:")) return null;
65267
+ const key = role.slice(7);
65268
+ return isRemoteCustomKey(key) ? key : null;
65269
+ }
65270
+ function isRemoteRole(value) {
65271
+ return isRemoteKnownRole(value) || customKeyOfRole(value) !== null;
65272
+ }
65273
+ var RemoteRoleSchema = zod.z.string().refine(isRemoteRole, { message: "a role is one of the known roles or custom:<key> (lowercase letters, digits, _)" });
65274
+ //#endregion
65275
+ //#region src/remote/remote-config.ts
65276
+ /**
65277
+ * The per-device configuration of the `remote` Customize extension (D963) and
65278
+ * the layout payload of the `remote` template kind. Pure data: no runtime, no
65279
+ * node dependency, so the admin UI imports the same schemas the server checks.
65280
+ *
65281
+ * A binding is the SAME shape an Action step has (`AutomationCapStepSchema`: a
65282
+ * cap method called on a device with arguments), narrowed to ONE device: an IR
65283
+ * code is `ir-transmitter.send` on the transmitter, a smart TV is a `switch` or a
65284
+ * vendor cap — the remote does not know which. How many times a command is sent
65285
+ * is the CALLER's (`sendCommand({ repeat })`), never stored here.
65286
+ */
65287
+ var RemoteBindingActionSchema = AutomationCapStepSchema.extend({ deviceId: zod.z.number().int().nonnegative() });
65288
+ var RemoteBindingSchema = zod.z.object({ action: RemoteBindingActionSchema });
65289
+ var RemoteCustomButtonSchema = zod.z.object({
65290
+ /** The slug after `custom:` in the role. */
65291
+ key: zod.z.string().refine(isRemoteCustomKey, { message: "a lowercase slug" }),
65292
+ /** What the operator calls it, and what a voice command may name. */
65293
+ name: zod.z.string().min(1).max(60)
65294
+ });
65295
+ var REMOTE_POWER_MODES = ["toggle", "discrete"];
65296
+ var RemotePowerModeSchema = zod.z.enum(REMOTE_POWER_MODES);
65297
+ var MAX_REMOTE_BINDINGS = 200;
65298
+ var MAX_REMOTE_CUSTOM_BUTTONS = 100;
65299
+ var RemoteConfigSchema = zod.z.object({
65300
+ layoutTemplateId: zod.z.string().min(1).nullable(),
65301
+ /**
65302
+ * The transmitter device new bindings and an import send through. A binding
65303
+ * names its own device, so one command may override it.
65304
+ */
65305
+ defaultTransmitterId: zod.z.number().int().nonnegative().nullable().default(null),
65306
+ /** Keyed by role. A role with no entry falls back to the native cap. */
65307
+ bindings: zod.z.record(zod.z.string(), RemoteBindingSchema),
65308
+ customButtons: zod.z.array(RemoteCustomButtonSchema).max(100),
65309
+ power: zod.z.object({ mode: RemotePowerModeSchema })
65310
+ }).superRefine((config, ctx) => {
65311
+ const roles = Object.keys(config.bindings);
65312
+ if (roles.length > 200) ctx.addIssue({
65313
+ code: "custom",
65314
+ message: "too many bindings",
65315
+ path: ["bindings"]
65316
+ });
65317
+ for (const role of roles) if (!isRemoteRole(role)) ctx.addIssue({
65318
+ code: "custom",
65319
+ message: `"${role}" is not a role`,
65320
+ path: ["bindings", role]
65321
+ });
65322
+ const keys = /* @__PURE__ */ new Set();
65323
+ config.customButtons.forEach((button, index) => {
65324
+ if (keys.has(button.key)) ctx.addIssue({
65325
+ code: "custom",
65326
+ message: `custom button "${button.key}" is listed twice`,
65327
+ path: [
65328
+ "customButtons",
65329
+ index,
65330
+ "key"
65331
+ ]
65332
+ });
65333
+ keys.add(button.key);
65334
+ });
65335
+ for (const role of roles) {
65336
+ const key = customKeyOfRole(role);
65337
+ if (key !== null && !keys.has(key)) ctx.addIssue({
65338
+ code: "custom",
65339
+ message: `${role} is bound but has no entry in customButtons`,
65340
+ path: ["bindings", role]
65341
+ });
65342
+ }
65343
+ });
65344
+ var EMPTY_REMOTE_CONFIG = {
65345
+ layoutTemplateId: null,
65346
+ defaultTransmitterId: null,
65347
+ bindings: {},
65348
+ customButtons: [],
65349
+ power: { mode: "discrete" }
65350
+ };
65351
+ /**
65352
+ * What the remote last SENT and therefore guesses about a device that does not
65353
+ * report it (infrared has no feedback). Persisted next to the config so a hub
65354
+ * restart does not forget it, and never recorded in history: it is a guess, not
65355
+ * a reading. A reading from a running source always outranks it.
65356
+ */
65357
+ var RemoteAssumedStateSchema = zod.z.object({
65358
+ power: zod.z.object({
65359
+ state: zod.z.enum(["on", "off"]),
65360
+ at: zod.z.number()
65361
+ }).nullable(),
65362
+ muted: zod.z.object({
65363
+ value: zod.z.boolean(),
65364
+ at: zod.z.number()
65365
+ }).nullable()
65366
+ });
65367
+ var EMPTY_REMOTE_ASSUMED_STATE = {
65368
+ power: null,
65369
+ muted: null
65370
+ };
65371
+ var MAX_LAYOUT_SPAN = 24;
65372
+ var RemoteLayoutButtonSchema = zod.z.object({
65373
+ id: zod.z.string().min(1).max(40),
65374
+ /** 0-based grid position. */
65375
+ row: zod.z.number().int().min(0).max(24),
65376
+ col: zod.z.number().int().min(0).max(24),
65377
+ rowSpan: zod.z.number().int().min(1).max(24),
65378
+ colSpan: zod.z.number().int().min(1).max(24),
65379
+ icon: zod.z.string().min(1).max(60).optional(),
65380
+ label: zod.z.string().min(1).max(40).optional(),
65381
+ role: RemoteRoleSchema
65382
+ });
65383
+ var RemoteLayoutPayloadSchema = zod.z.object({
65384
+ columns: zod.z.number().int().min(1).max(24),
65385
+ rows: zod.z.number().int().min(1).max(24),
65386
+ buttons: zod.z.array(RemoteLayoutButtonSchema).max(200)
65387
+ }).superRefine((grid, ctx) => {
65388
+ const ids = /* @__PURE__ */ new Set();
65389
+ const taken = /* @__PURE__ */ new Map();
65390
+ grid.buttons.forEach((button, index) => {
65391
+ if (ids.has(button.id)) ctx.addIssue({
65392
+ code: "custom",
65393
+ message: `button id "${button.id}" is used twice`,
65394
+ path: [
65395
+ "buttons",
65396
+ index,
65397
+ "id"
65398
+ ]
65399
+ });
65400
+ ids.add(button.id);
65401
+ if (button.row + button.rowSpan > grid.rows || button.col + button.colSpan > grid.columns) {
65402
+ ctx.addIssue({
65403
+ code: "custom",
65404
+ message: `button "${button.id}" leaves the grid`,
65405
+ path: ["buttons", index]
65406
+ });
65407
+ return;
65408
+ }
65409
+ for (let r = button.row; r < button.row + button.rowSpan; r += 1) for (let c = button.col; c < button.col + button.colSpan; c += 1) {
65410
+ const cell = `${r}:${c}`;
65411
+ const other = taken.get(cell);
65412
+ if (other !== void 0) {
65413
+ ctx.addIssue({
65414
+ code: "custom",
65415
+ message: `buttons "${other}" and "${button.id}" overlap`,
65416
+ path: ["buttons", index]
65417
+ });
65418
+ return;
65419
+ }
65420
+ taken.set(cell, button.id);
65421
+ }
65422
+ });
65423
+ });
65424
+ //#endregion
65425
+ //#region src/capabilities/remote.cap.ts
65426
+ /**
65427
+ * remote — a button panel over a device (D963). The operator binds a ROLE
65428
+ * (`power`, `volume_up`, `custom:source_hdmi_1`, …) to an action — a cap method
65429
+ * called on a device, the shape an Action step has — and picks a layout
65430
+ * template that places the roles on a grid. What a button does is the action's
65431
+ * business: an infrared code is `ir-transmitter.send` on the transmitter, a smart
65432
+ * TV is whatever cap drives it.
65433
+ *
65434
+ * It is a Customize extension (D944) of a media player: the configuration is
65435
+ * the operator's, stored per device, and the editor is derived from the
65436
+ * `config` target below. A media-player command whose role is bound here goes
65437
+ * through the binding for EVERY caller (UI, automation, exporter); an unbound
65438
+ * role falls back to the native cap.
65439
+ *
65440
+ * `sendCommand` answers a typed result and never throws for an expected
65441
+ * refusal, so a caller tells "that role is not bound" from "the board is off"
65442
+ * without parsing text. How many times a command is sent is the CALLER's
65443
+ * (`repeat`, volume steps), never a stored setting.
65444
+ */
65445
+ var REMOTE_MAX_REPEAT = 50;
65446
+ var RemoteBoundCommandSchema = zod.z.object({
65447
+ role: RemoteRoleSchema,
65448
+ /** The operator's name for a custom button; absent for a known role. */
65449
+ label: zod.z.string().optional(),
65450
+ custom: zod.z.boolean()
65451
+ });
65452
+ var RemoteCommandListSchema = zod.z.object({
65453
+ layoutTemplateId: zod.z.string().nullable(),
65454
+ power: zod.z.object({ mode: RemotePowerModeSchema }),
65455
+ /** Only the roles that are BOUND — the ones a press can reach. */
65456
+ commands: zod.z.array(RemoteBoundCommandSchema)
65457
+ });
65458
+ var REMOTE_REFUSAL_REASONS = [
65459
+ "no-remote",
65460
+ "unbound-role",
65461
+ "action-failed"
65462
+ ];
65463
+ var RemoteRefusalReasonSchema = zod.z.enum(REMOTE_REFUSAL_REASONS);
65464
+ var RemoteSendResultSchema = zod.z.discriminatedUnion("status", [
65465
+ zod.z.object({
65466
+ status: zod.z.literal("sent"),
65467
+ /** How many times the action ran. */
65468
+ sends: zod.z.number().int().min(1)
65469
+ }),
65470
+ zod.z.object({
65471
+ status: zod.z.literal("skipped"),
65472
+ reason: zod.z.literal("already-in-state")
65473
+ }),
65474
+ zod.z.object({
65475
+ status: zod.z.literal("refused"),
65476
+ reason: RemoteRefusalReasonSchema,
65477
+ detail: zod.z.string().optional()
65478
+ })
65479
+ ]);
65480
+ /** The media-player commands a bound remote role takes over (precedence, D963). */
65481
+ var REMOTE_MEDIA_COMMANDS = [
65482
+ "play",
65483
+ "pause",
65484
+ "stop",
65485
+ "next",
65486
+ "previous",
65487
+ "setMute",
65488
+ "selectSource"
65489
+ ];
65490
+ var RemoteMediaCommandSchema = zod.z.enum(REMOTE_MEDIA_COMMANDS);
65491
+ /** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
65492
+ var RemoteMediaCommandResultSchema = zod.z.discriminatedUnion("status", [...RemoteSendResultSchema.options, zod.z.object({ status: zod.z.literal("not-routed") })]);
65493
+ var REMOTE_CONFIG_REFUSALS = ["unknown-template", "not-a-media-player"];
65494
+ var RemoteSetConfigResultSchema = zod.z.discriminatedUnion("status", [zod.z.object({ status: zod.z.literal("saved") }), zod.z.object({
65495
+ status: zod.z.literal("refused"),
65496
+ reason: zod.z.enum(REMOTE_CONFIG_REFUSALS),
65497
+ detail: zod.z.string()
65498
+ })]);
65499
+ var deviceInput = zod.z.object({ deviceId: zod.z.number().int().nonnegative() });
65500
+ var remoteCustomize = {
65501
+ version: 1,
65502
+ targets: [{
65503
+ id: "remote",
65504
+ cardinality: "config",
65505
+ vocabulary: { cap: "remote" },
65506
+ deviceTypes: [require_err_msg.DeviceType.MediaPlayer],
65507
+ apply: "add",
65508
+ readMethod: "getConfig",
65509
+ writeMethod: "setConfig"
65510
+ }]
65511
+ };
65512
+ var remoteCapability = {
65513
+ name: "remote",
65514
+ scope: "system",
65515
+ mode: "singleton",
65516
+ methods: {
65517
+ getConfig: require_sleep.method(deviceInput, RemoteConfigSchema, { auth: "admin" }),
65518
+ setConfig: require_sleep.method(deviceInput.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
65519
+ kind: "mutation",
65520
+ auth: "admin"
65521
+ }),
65522
+ listCommands: require_sleep.method(deviceInput, RemoteCommandListSchema, { auth: "protected" }),
65523
+ sendCommand: require_sleep.method(deviceInput.extend({
65524
+ role: RemoteRoleSchema,
65525
+ /** Sends in a row (volume steps). */
65526
+ repeat: zod.z.number().int().min(1).max(50).default(1)
65527
+ }), RemoteSendResultSchema, {
65528
+ kind: "mutation",
65529
+ auth: "protected"
65530
+ }),
65531
+ /**
65532
+ * A media-player command, offered to the remote first. Answers `not-routed`
65533
+ * when the command's role is not bound, so the caller runs the native cap.
65534
+ * The dispatch redirect calls this for every caller (UI, automation,
65535
+ * exporter); `repeat` is the caller's count where it has one.
65536
+ */
65537
+ sendMediaCommand: require_sleep.method(deviceInput.extend({
65538
+ command: RemoteMediaCommandSchema,
65539
+ muted: zod.z.boolean().optional(),
65540
+ source: zod.z.string().optional(),
65541
+ repeat: zod.z.number().int().min(1).max(50).default(1)
65542
+ }), RemoteMediaCommandResultSchema, {
65543
+ kind: "mutation",
65544
+ auth: "protected"
65545
+ })
65546
+ },
65547
+ customize: remoteCustomize
65548
+ };
65549
+ //#endregion
65550
+ //#region src/template/template-kinds.ts
65551
+ /**
65552
+ * Template kinds (D963). A template is a named, reusable piece of operator
65553
+ * data that more than one device points at — today the button grid of a
65554
+ * remote. The store is generic: it keeps `{ id, kind, name, payload }` and
65555
+ * knows nothing of a remote. Only the SYSTEM declares kinds, here: each kind
65556
+ * names the schema its payload must satisfy. Adding a kind is adding a member
65557
+ * to `TEMPLATE_KINDS` and a payload schema; the compiler refuses one without
65558
+ * the other.
65559
+ */
65560
+ var TEMPLATE_KINDS = ["remote"];
65561
+ var TemplateKindSchema = zod.z.enum(TEMPLATE_KINDS);
65562
+ var TEMPLATE_PAYLOAD_SCHEMAS = { remote: RemoteLayoutPayloadSchema };
65563
+ function isTemplateKind(value) {
65564
+ return TemplateKindSchema.safeParse(value).success;
65565
+ }
65566
+ /** The payload as the kind's schema reads it, or why it is not one. */
65567
+ function checkTemplatePayload(kind, payload) {
65568
+ const parsed = TEMPLATE_PAYLOAD_SCHEMAS[kind].safeParse(payload);
65569
+ if (parsed.success) return {
65570
+ ok: true,
65571
+ payload: parsed.data
65572
+ };
65573
+ return {
65574
+ ok: false,
65575
+ detail: parsed.error.issues.map((i) => i.message).join("; ")
65576
+ };
65577
+ }
65578
+ //#endregion
65579
+ //#region src/capabilities/template.cap.ts
65580
+ /**
65581
+ * template — the generic store of reusable, named operator data (D963). A
65582
+ * record is `{ id, kind, name, payload }`; the kinds (today only `remote`, the
65583
+ * button grid of a remote) and the schema each payload must satisfy are
65584
+ * declared by the system in `template-kinds.ts`. Nothing here is specific to a
65585
+ * kind: the engine validates a payload against its kind's schema and, on a
65586
+ * delete, asks the kind's owner whether anything still points at the record.
65587
+ *
65588
+ * Every refusal is a typed result — a UI says "unknown kind" or names the
65589
+ * devices that still use a template, without parsing text.
65590
+ */
65591
+ var TemplateRecordSchema = zod.z.object({
65592
+ id: zod.z.string().min(1),
65593
+ kind: TemplateKindSchema,
65594
+ name: zod.z.string().min(1).max(120),
65595
+ /** Shaped by the kind; `checkTemplatePayload(kind, payload)` reads it. */
65596
+ payload: zod.z.unknown(),
65597
+ createdAt: zod.z.number(),
65598
+ updatedAt: zod.z.number()
65599
+ });
65600
+ var TEMPLATE_WRITE_REFUSALS = ["invalid-payload", "not-found"];
65601
+ var TemplateWriteResultSchema = zod.z.discriminatedUnion("status", [zod.z.object({
65602
+ status: zod.z.literal("saved"),
65603
+ template: TemplateRecordSchema
65604
+ }), zod.z.object({
65605
+ status: zod.z.literal("refused"),
65606
+ reason: zod.z.enum(TEMPLATE_WRITE_REFUSALS),
65607
+ detail: zod.z.string()
65608
+ })]);
65609
+ /** A device still pointing at a template. */
65610
+ var TemplateUserSchema = zod.z.object({
65611
+ deviceId: zod.z.number().int().nonnegative(),
65612
+ name: zod.z.string()
65613
+ });
65614
+ var TemplateDeleteResultSchema = zod.z.discriminatedUnion("status", [
65615
+ zod.z.object({ status: zod.z.literal("deleted") }),
65616
+ zod.z.object({ status: zod.z.literal("not-found") }),
65617
+ zod.z.object({
65618
+ status: zod.z.literal("in-use"),
65619
+ devices: zod.z.array(TemplateUserSchema).min(1)
65620
+ })
65621
+ ]);
65622
+ var templateCapability = {
65623
+ name: "template",
65624
+ scope: "system",
65625
+ mode: "collection",
65626
+ methods: {
65627
+ list: require_sleep.method(zod.z.object({ kind: TemplateKindSchema.optional() }), zod.z.array(TemplateRecordSchema), { auth: "protected" }),
65628
+ get: require_sleep.method(zod.z.object({ id: zod.z.string().min(1) }), TemplateRecordSchema.nullable(), { auth: "protected" }),
65629
+ create: require_sleep.method(zod.z.object({
65630
+ kind: TemplateKindSchema,
65631
+ name: zod.z.string().min(1).max(120),
65632
+ payload: zod.z.unknown()
65633
+ }), TemplateWriteResultSchema, {
65634
+ kind: "mutation",
65635
+ auth: "admin"
65636
+ }),
65637
+ update: require_sleep.method(zod.z.object({
65638
+ id: zod.z.string().min(1),
65639
+ name: zod.z.string().min(1).max(120).optional(),
65640
+ payload: zod.z.unknown().optional()
65641
+ }), TemplateWriteResultSchema, {
65642
+ kind: "mutation",
65643
+ auth: "admin"
65644
+ }),
65645
+ delete: require_sleep.method(zod.z.object({ id: zod.z.string().min(1) }), TemplateDeleteResultSchema, {
65646
+ kind: "mutation",
65647
+ auth: "admin"
65648
+ })
65649
+ }
65650
+ };
65651
+ //#endregion
65202
65652
  //#region src/capabilities/button.cap.ts
65203
65653
  /**
65204
65654
  * button — device-scoped capability for HA `button.*` / `input_button.*`
@@ -69997,9 +70447,35 @@ var MediaInfoSchema = zod.z.object({
69997
70447
  /** Optional cover-art / thumbnail URL. */
69998
70448
  imageUrl: zod.z.string().optional()
69999
70449
  });
70450
+ /**
70451
+ * Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
70452
+ * the device said so. `assumed`: the driver has no feedback (infrared) and
70453
+ * tracks what it last SENT; it moves only after a send was accepted, and a
70454
+ * human at the real remote makes it wrong. A UI shows an assumed value as a
70455
+ * guess, never as a reading.
70456
+ */
70457
+ var MediaPlayerStateSourceSchema = zod.z.enum(["reported", "assumed"]);
70458
+ /** The fields of a media-player status a driver can only guess. */
70459
+ var MediaPlayerAssumedFieldSchema = zod.z.enum([
70460
+ "state",
70461
+ "volumeLevel",
70462
+ "isMuted",
70463
+ "source"
70464
+ ]);
70000
70465
  var MediaPlayerStatusSchema = zod.z.object({
70001
70466
  /** Playback lifecycle state. */
70002
70467
  state: MediaPlayerStateSchema,
70468
+ /**
70469
+ * Whether the state fields are readings or the driver's own bookkeeping.
70470
+ * A slice written before this field existed parses as `reported`.
70471
+ */
70472
+ stateSource: MediaPlayerStateSourceSchema.default("reported"),
70473
+ /**
70474
+ * Which fields are the driver's own guess. `stateSource` is `assumed` when
70475
+ * this is not empty; a field not listed here is a reading even then (power
70476
+ * from a running-state source, mute guessed from the last send).
70477
+ */
70478
+ assumedFields: zod.z.array(MediaPlayerAssumedFieldSchema).default([]),
70003
70479
  /** Volume as 0..100 inclusive. Null when the device has no volume
70004
70480
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
70005
70481
  volumeLevel: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
@@ -72483,6 +72959,14 @@ var PetFeederStatusSchema = zod.z.object({
72483
72959
  feedSound: zod.z.boolean(),
72484
72960
  /** Speaker / prompt volume level (device-scaled integer). */
72485
72961
  volume: zod.z.number().meta(require_sleep.readingMeta("none")),
72962
+ /** Portion (grams) of the last manual feed sent from a single-hopper
72963
+ * stepper — the default the next manual feed starts from. Persisted per
72964
+ * device by the provider; null until a manual feed has been sent. */
72965
+ lastFeedGrams: gramsPortion.nullable(),
72966
+ /** Hopper-1 portion of the last manual feed (dual-hopper); null until sent. */
72967
+ lastFeedHopper1: gramsPortion.nullable(),
72968
+ /** Hopper-2 portion of the last manual feed (dual-hopper); null until sent. */
72969
+ lastFeedHopper2: gramsPortion.nullable(),
72486
72970
  /** Ms epoch when the slice was last refreshed from the cloud. */
72487
72971
  lastFetchedAt: zod.z.number()
72488
72972
  });
@@ -81092,6 +81576,186 @@ var PIPELINE_OWNER_CAPABILITY_NAMES = [
81092
81576
  "pipeline-runner"
81093
81577
  ];
81094
81578
  //#endregion
81579
+ //#region src/ir/ir-error.ts
81580
+ var IrCodecError = class extends Error {
81581
+ code;
81582
+ constructor(code, message) {
81583
+ super(message);
81584
+ this.name = "IrCodecError";
81585
+ this.code = code;
81586
+ }
81587
+ };
81588
+ //#endregion
81589
+ //#region src/ir/pronto.ts
81590
+ /** One Pronto frequency-word unit, in microseconds, per unit of the word. */
81591
+ var PRONTO_CLOCK_US = .241246;
81592
+ var LEARNED_FORM = 0;
81593
+ /**
81594
+ * Decode a learned (0000 form) Pronto hex string. The intro burst is followed by
81595
+ * the repeat burst once; a different form is refused with `pronto-unsupported-form`.
81596
+ */
81597
+ function decodePronto(code) {
81598
+ const words = code.trim().split(/\s+/).filter((w) => w.length > 0);
81599
+ if (words.length < 4) throw new IrCodecError("pronto-malformed", `Pronto code needs at least 4 words, got ${words.length}`);
81600
+ const values = words.map((w) => /^[0-9a-fA-F]{1,4}$/.test(w) ? parseInt(w, 16) : NaN);
81601
+ if (values.some((v) => Number.isNaN(v))) throw new IrCodecError("pronto-malformed", "Pronto code must be 4-digit hexadecimal words");
81602
+ const [form, freqWord, introCount, repeatCount] = values;
81603
+ if (form === void 0 || freqWord === void 0 || introCount === void 0 || repeatCount === void 0) throw new IrCodecError("pronto-malformed", "Pronto header incomplete");
81604
+ if (form !== LEARNED_FORM) throw new IrCodecError("pronto-unsupported-form", `Pronto form ${words[0] ?? ""} is not supported, only the learned 0000 form`);
81605
+ if (freqWord === 0) throw new IrCodecError("pronto-malformed", "Pronto frequency word is zero");
81606
+ const pairs = introCount + repeatCount;
81607
+ if (pairs === 0 || values.length !== 4 + pairs * 2) throw new IrCodecError("pronto-malformed", `Pronto declares ${pairs} burst pairs but carries ${(values.length - 4) / 2} word pairs`);
81608
+ const unitUs = freqWord * PRONTO_CLOCK_US;
81609
+ const timings = values.slice(4).map((count, index) => {
81610
+ const us = Math.max(1, Math.round(count * unitUs));
81611
+ return index % 2 === 0 ? us : -us;
81612
+ });
81613
+ const checked = IrTimingsSchema.safeParse(timings);
81614
+ if (!checked.success) throw new IrCodecError("invalid-timings", `Pronto decoded to invalid timings: ${checked.error.message}`);
81615
+ return {
81616
+ timings: checked.data,
81617
+ carrierHz: Math.round(1e6 / unitUs)
81618
+ };
81619
+ }
81620
+ //#endregion
81621
+ //#region src/ir/ir-payload.ts
81622
+ /** What a remote command stores: a raw pulse train, or a learned Pronto code. */
81623
+ var IrPayloadSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
81624
+ kind: zod.z.literal("raw"),
81625
+ timings: zod.z.array(zod.z.number().int()),
81626
+ settings: TransmitSettingsSchema.optional()
81627
+ }), zod.z.object({
81628
+ kind: zod.z.literal("pronto"),
81629
+ code: zod.z.string()
81630
+ })]);
81631
+ /** Resolve a payload to what `ir-transmitter.send` takes. Throws `IrCodecError`. */
81632
+ function toTimings(payload) {
81633
+ if (payload.kind === "pronto") {
81634
+ const decoded = decodePronto(payload.code);
81635
+ return {
81636
+ timings: decoded.timings,
81637
+ settings: { carrierHz: decoded.carrierHz }
81638
+ };
81639
+ }
81640
+ const checked = IrTimingsSchema.safeParse(payload.timings);
81641
+ if (!checked.success) throw new IrCodecError("invalid-timings", checked.error.message);
81642
+ return {
81643
+ timings: checked.data,
81644
+ settings: payload.settings ?? {}
81645
+ };
81646
+ }
81647
+ //#endregion
81648
+ //#region src/remote/smartir-draft.ts
81649
+ /**
81650
+ * Turns a parsed SmartIR command table (`parseSmartIr`, the one parser in
81651
+ * `ir/`) into a draft of the `remote` configuration (D963): the role each
81652
+ * command fills and the binding that sends its pulse train through an
81653
+ * `ir-transmitter`.
81654
+ *
81655
+ * PURE and browser-safe: the admin UI parses the file the operator picked,
81656
+ * builds this draft and saves it on the cap; the server never receives a code
81657
+ * file. A command whose payload cannot be resolved to a pulse train comes back
81658
+ * in `skipped` with the reason, never dropped silently.
81659
+ */
81660
+ /** SmartIR's own command keys and the role each one fills. */
81661
+ var KNOWN_KEY_ROLES = {
81662
+ previousChannel: "channel_down",
81663
+ nextChannel: "channel_up",
81664
+ volumeDown: "volume_down",
81665
+ volumeUp: "volume_up",
81666
+ mute: "mute"
81667
+ };
81668
+ function slug(text) {
81669
+ const base = text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
81670
+ return base.length > 0 ? base.slice(0, 40) : "button";
81671
+ }
81672
+ function sameTrain(a, b) {
81673
+ return a.timings.length === b.timings.length && a.timings.every((p, i) => p === b.timings[i]);
81674
+ }
81675
+ function bindingOf(command) {
81676
+ const hasSettings = Object.keys(command.settings).length > 0;
81677
+ return { action: {
81678
+ kind: "cap",
81679
+ deviceId: command.transmitterDeviceId,
81680
+ cap: "ir-transmitter",
81681
+ method: "send",
81682
+ args: {
81683
+ timings: command.timings,
81684
+ ...hasSettings ? { settings: command.settings } : {}
81685
+ }
81686
+ } };
81687
+ }
81688
+ function resolveAll(table, target, skipped) {
81689
+ const out = [];
81690
+ for (const command of table.commands) try {
81691
+ const { timings, settings } = toTimings(command.payload);
81692
+ out.push({
81693
+ name: command.name,
81694
+ group: command.group,
81695
+ timings,
81696
+ settings,
81697
+ transmitterDeviceId: command.transmitterDeviceId ?? target.transmitterDeviceId
81698
+ });
81699
+ } catch (err) {
81700
+ if (!(err instanceof IrCodecError)) throw err;
81701
+ skipped.push({
81702
+ name: command.name,
81703
+ reason: err.message
81704
+ });
81705
+ }
81706
+ return out;
81707
+ }
81708
+ /**
81709
+ * `on` and `off` carrying the SAME pulse train are one power TOGGLE (bound to
81710
+ * `power`); different trains are a discrete pair (`power_on` / `power_off`). A
81711
+ * command SmartIR knows fills its role; a source, or any command it does not
81712
+ * know, becomes a custom button named as the file spells it.
81713
+ */
81714
+ function smartIrDraft(table, target) {
81715
+ const skipped = [];
81716
+ const commands = resolveAll(table, target, skipped);
81717
+ const bindings = {};
81718
+ const customButtons = [];
81719
+ const usedKeys = /* @__PURE__ */ new Set();
81720
+ const addCustom = (command, prefix) => {
81721
+ let key = prefix + slug(command.name);
81722
+ for (let n = 2; usedKeys.has(key); n += 1) key = `${prefix}${slug(command.name)}_${n}`;
81723
+ usedKeys.add(key);
81724
+ customButtons.push({
81725
+ key,
81726
+ name: command.name
81727
+ });
81728
+ bindings[customRole(key)] = bindingOf(command);
81729
+ };
81730
+ const find = (name) => commands.find((c) => c.group === "command" && c.name === name);
81731
+ const on = find("on");
81732
+ const off = find("off");
81733
+ let mode = "discrete";
81734
+ if (on !== void 0 && off !== void 0 && sameTrain(on, off)) {
81735
+ mode = "toggle";
81736
+ bindings["power"] = bindingOf(on);
81737
+ } else {
81738
+ if (on !== void 0) bindings["power_on"] = bindingOf(on);
81739
+ if (off !== void 0) bindings["power_off"] = bindingOf(off);
81740
+ }
81741
+ for (const command of commands) {
81742
+ if (command.group === "source") {
81743
+ addCustom(command, "source_");
81744
+ continue;
81745
+ }
81746
+ if (command.name === "on" || command.name === "off") continue;
81747
+ const role = KNOWN_KEY_ROLES[command.name];
81748
+ if (role !== void 0) bindings[role] = bindingOf(command);
81749
+ else addCustom(command, "");
81750
+ }
81751
+ return {
81752
+ bindings,
81753
+ customButtons,
81754
+ power: { mode },
81755
+ skipped
81756
+ };
81757
+ }
81758
+ //#endregion
81095
81759
  //#region src/running-state/from-operation.ts
81096
81760
  function programStartFromRemaining(running, remainingSeconds, fetchedAt) {
81097
81761
  if (!running || remainingSeconds === null || remainingSeconds === void 0) return null;
@@ -82715,7 +83379,7 @@ function convertUnitTargets(from) {
82715
83379
  //#region src/generated/customize-catalog.ts
82716
83380
  /**
82717
83381
  * Customize catalog, projected from every `*.cap.ts` `customize` block.
82718
- * 6 caps. Field keys are `runtimeState` paths. No Zod.
83382
+ * 7 caps. Field keys are `runtimeState` paths. No Zod.
82719
83383
  */
82720
83384
  var CAP_CUSTOMIZE_CATALOG = {
82721
83385
  "battery": {
@@ -83142,6 +83806,21 @@ var CAP_CUSTOMIZE_CATALOG = {
83142
83806
  }
83143
83807
  ]
83144
83808
  },
83809
+ "remote": {
83810
+ version: 1,
83811
+ targets: [{
83812
+ id: "remote",
83813
+ cardinality: "config",
83814
+ deviceTypes: [require_err_msg.DeviceType.MediaPlayer],
83815
+ apply: "add",
83816
+ readMethod: "getConfig",
83817
+ writeMethod: "setConfig",
83818
+ vocabularyCap: "remote",
83819
+ sources: [],
83820
+ sourceReport: { status: "assignment-status" },
83821
+ advancedExpression: false
83822
+ }]
83823
+ },
83145
83824
  "running-state": {
83146
83825
  version: 1,
83147
83826
  targets: [{
@@ -95092,6 +95771,41 @@ var METHOD_ACCESS_MAP = Object.freeze({
95092
95771
  access: "create",
95093
95772
  role: "admin"
95094
95773
  },
95774
+ "remote.getConfig": {
95775
+ capName: "remote",
95776
+ capScope: "system",
95777
+ addonId: null,
95778
+ access: "view",
95779
+ role: "admin"
95780
+ },
95781
+ "remote.listCommands": {
95782
+ capName: "remote",
95783
+ capScope: "system",
95784
+ addonId: null,
95785
+ access: "view",
95786
+ role: "admin"
95787
+ },
95788
+ "remote.sendCommand": {
95789
+ capName: "remote",
95790
+ capScope: "system",
95791
+ addonId: null,
95792
+ access: "create",
95793
+ role: "admin"
95794
+ },
95795
+ "remote.sendMediaCommand": {
95796
+ capName: "remote",
95797
+ capScope: "system",
95798
+ addonId: null,
95799
+ access: "create",
95800
+ role: "admin"
95801
+ },
95802
+ "remote.setConfig": {
95803
+ capName: "remote",
95804
+ capScope: "system",
95805
+ addonId: null,
95806
+ access: "create",
95807
+ role: "admin"
95808
+ },
95095
95809
  "runningState.getStatus": {
95096
95810
  capName: "running-state",
95097
95811
  capScope: "device",
@@ -96282,6 +96996,41 @@ var METHOD_ACCESS_MAP = Object.freeze({
96282
96996
  access: "view",
96283
96997
  role: "viewer"
96284
96998
  },
96999
+ "template.create": {
97000
+ capName: "template",
97001
+ capScope: "system",
97002
+ addonId: null,
97003
+ access: "create",
97004
+ role: "admin"
97005
+ },
97006
+ "template.delete": {
97007
+ capName: "template",
97008
+ capScope: "system",
97009
+ addonId: null,
97010
+ access: "delete",
97011
+ role: "admin"
97012
+ },
97013
+ "template.get": {
97014
+ capName: "template",
97015
+ capScope: "system",
97016
+ addonId: null,
97017
+ access: "view",
97018
+ role: "admin"
97019
+ },
97020
+ "template.list": {
97021
+ capName: "template",
97022
+ capScope: "system",
97023
+ addonId: null,
97024
+ access: "view",
97025
+ role: "admin"
97026
+ },
97027
+ "template.update": {
97028
+ capName: "template",
97029
+ capScope: "system",
97030
+ addonId: null,
97031
+ access: "create",
97032
+ role: "admin"
97033
+ },
96285
97034
  "terminalSession.adoptLegacyMonitor": {
96286
97035
  capName: "terminal-session",
96287
97036
  capScope: "system",
@@ -97396,6 +98145,7 @@ var KNOWN_CAP_NAMES = [
97396
98145
  "recording-onboard",
97397
98146
  "recording-signal",
97398
98147
  "records",
98148
+ "remote",
97399
98149
  "running-state",
97400
98150
  "scene-monitor",
97401
98151
  "script-runner",
@@ -97419,6 +98169,7 @@ var KNOWN_CAP_NAMES = [
97419
98169
  "system",
97420
98170
  "tamper",
97421
98171
  "temperature-sensor",
98172
+ "template",
97422
98173
  "terminal-session",
97423
98174
  "toast",
97424
98175
  "training",
@@ -97612,6 +98363,7 @@ var SYSTEM_CAP_NAMES = [
97612
98363
  "recording-archive",
97613
98364
  "recording-export",
97614
98365
  "records",
98366
+ "remote",
97615
98367
  "server-management",
97616
98368
  "settings-store",
97617
98369
  "smtp-provider",
@@ -97624,6 +98376,7 @@ var SYSTEM_CAP_NAMES = [
97624
98376
  "storage-provider",
97625
98377
  "stream-broker",
97626
98378
  "system",
98379
+ "template",
97627
98380
  "terminal-session",
97628
98381
  "toast",
97629
98382
  "training",
@@ -101510,6 +102263,7 @@ var SYSTEM_CAP_VOCABULARY = {
101510
102263
  "recording-archive": system(STORAGE),
101511
102264
  "recording-export": system("recording exports — requested from the timeline, not an automation step"),
101512
102265
  records: system(INTERNAL),
102266
+ remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
101513
102267
  "server-management": system(CLUSTER),
101514
102268
  "settings-store": system(INTERNAL),
101515
102269
  "smtp-provider": system(INTERNAL),
@@ -101522,6 +102276,7 @@ var SYSTEM_CAP_VOCABULARY = {
101522
102276
  "storage-provider": system(STORAGE),
101523
102277
  "stream-broker": system(PIPELINE),
101524
102278
  system: system(CLUSTER),
102279
+ template: system("operator-authored templates — edited in their own editor, never actuated"),
101525
102280
  "terminal-session": system(CLUSTER),
101526
102281
  toast: system(UI_PLUMBING),
101527
102282
  training: system(ML),
@@ -103491,6 +104246,7 @@ var CAPABILITY_NAMES = {
103491
104246
  recordingOnboard: "recording-onboard",
103492
104247
  recordingSignal: "recording-signal",
103493
104248
  records: "records",
104249
+ remote: "remote",
103494
104250
  runningState: "running-state",
103495
104251
  sceneMonitor: "scene-monitor",
103496
104252
  scriptRunner: "script-runner",
@@ -103514,6 +104270,7 @@ var CAPABILITY_NAMES = {
103514
104270
  system: "system",
103515
104271
  tamper: "tamper",
103516
104272
  temperatureSensor: "temperature-sensor",
104273
+ template: "template",
103517
104274
  terminalSession: "terminal-session",
103518
104275
  toast: "toast",
103519
104276
  training: "training",
@@ -104117,6 +104874,10 @@ var CAPABILITY_ROUTER_KEYS = [
104117
104874
  key: "records",
104118
104875
  name: "records"
104119
104876
  },
104877
+ {
104878
+ key: "remote",
104879
+ name: "remote"
104880
+ },
104120
104881
  {
104121
104882
  key: "runningState",
104122
104883
  name: "running-state"
@@ -104209,6 +104970,10 @@ var CAPABILITY_ROUTER_KEYS = [
104209
104970
  key: "temperatureSensor",
104210
104971
  name: "temperature-sensor"
104211
104972
  },
104973
+ {
104974
+ key: "template",
104975
+ name: "template"
104976
+ },
104212
104977
  {
104213
104978
  key: "terminalSession",
104214
104979
  name: "terminal-session"
@@ -104445,6 +105210,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
104445
105210
  recordingOnboardCapability,
104446
105211
  recordingSignalCapability,
104447
105212
  recordsCapability,
105213
+ remoteCapability,
104448
105214
  runningStateCapability,
104449
105215
  sceneMonitorCapability,
104450
105216
  scriptRunnerCapability,
@@ -104468,6 +105234,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
104468
105234
  systemCapability,
104469
105235
  tamperCapability,
104470
105236
  temperatureSensorCapability,
105237
+ templateCapability,
104471
105238
  terminalSessionCapability,
104472
105239
  toastCapability,
104473
105240
  trainingCapability,
@@ -106817,6 +107584,31 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
106817
107584
  form: "single",
106818
107585
  optional: true
106819
107586
  }],
107587
+ "remote.getConfig": [{
107588
+ name: "deviceId",
107589
+ form: "single",
107590
+ optional: false
107591
+ }],
107592
+ "remote.listCommands": [{
107593
+ name: "deviceId",
107594
+ form: "single",
107595
+ optional: false
107596
+ }],
107597
+ "remote.sendCommand": [{
107598
+ name: "deviceId",
107599
+ form: "single",
107600
+ optional: false
107601
+ }],
107602
+ "remote.sendMediaCommand": [{
107603
+ name: "deviceId",
107604
+ form: "single",
107605
+ optional: false
107606
+ }],
107607
+ "remote.setConfig": [{
107608
+ name: "deviceId",
107609
+ form: "single",
107610
+ optional: false
107611
+ }],
106820
107612
  "runningState.getStatus": [{
106821
107613
  name: "deviceId",
106822
107614
  form: "single",
@@ -107474,6 +108266,11 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
107474
108266
  "records.histogram",
107475
108267
  "records.putMany",
107476
108268
  "records.query",
108269
+ "remote.getConfig",
108270
+ "remote.listCommands",
108271
+ "remote.sendCommand",
108272
+ "remote.sendMediaCommand",
108273
+ "remote.setConfig",
107477
108274
  "storageAdmin.readRecords",
107478
108275
  "streamBroker.acquireEgressTranscode",
107479
108276
  "streamBroker.applyDeviceSettingsPatch",
@@ -109159,6 +109956,13 @@ function createSystemProxy(api) {
109159
109956
  getDownloadUrl: (input) => dispatch("recordingExport", "getDownloadUrl", "query", input),
109160
109957
  readExportBytes: (input) => dispatch("recordingExport", "readExportBytes", "query", input)
109161
109958
  },
109959
+ remote: {
109960
+ getConfig: (input) => dispatch("remote", "getConfig", "query", input),
109961
+ setConfig: (input) => dispatch("remote", "setConfig", "mutation", input),
109962
+ listCommands: (input) => dispatch("remote", "listCommands", "query", input),
109963
+ sendCommand: (input) => dispatch("remote", "sendCommand", "mutation", input),
109964
+ sendMediaCommand: (input) => dispatch("remote", "sendMediaCommand", "mutation", input)
109965
+ },
109162
109966
  serverManagement: {
109163
109967
  getServerPackageStatus: (input) => dispatch("serverManagement", "getServerPackageStatus", "query", input),
109164
109968
  checkServerUpdate: (input) => dispatch("serverManagement", "checkServerUpdate", "mutation", input),
@@ -109266,6 +110070,13 @@ function createSystemProxy(api) {
109266
110070
  getLoggingSettings: (input) => dispatch("system", "getLoggingSettings", "query", input),
109267
110071
  setLoggingSettings: (input) => dispatch("system", "setLoggingSettings", "mutation", input)
109268
110072
  },
110073
+ template: {
110074
+ list: (input) => dispatch("template", "list", "query", input),
110075
+ get: (input) => dispatch("template", "get", "query", input),
110076
+ create: (input) => dispatch("template", "create", "mutation", input),
110077
+ update: (input) => dispatch("template", "update", "mutation", input),
110078
+ delete: (input) => dispatch("template", "delete", "mutation", input)
110079
+ },
109269
110080
  terminalSession: {
109270
110081
  listProfiles: (input) => dispatch("terminalSession", "listProfiles", "query", input),
109271
110082
  listInstances: (input) => dispatch("terminalSession", "listInstances", "query", input),
@@ -119852,75 +120663,6 @@ function armRemovalReconcile(opts) {
119852
120663
  return () => clearInterval(timer);
119853
120664
  }
119854
120665
  //#endregion
119855
- //#region src/ir/ir-error.ts
119856
- var IrCodecError = class extends Error {
119857
- code;
119858
- constructor(code, message) {
119859
- super(message);
119860
- this.name = "IrCodecError";
119861
- this.code = code;
119862
- }
119863
- };
119864
- //#endregion
119865
- //#region src/ir/pronto.ts
119866
- /** One Pronto frequency-word unit, in microseconds, per unit of the word. */
119867
- var PRONTO_CLOCK_US = .241246;
119868
- var LEARNED_FORM = 0;
119869
- /**
119870
- * Decode a learned (0000 form) Pronto hex string. The intro burst is followed by
119871
- * the repeat burst once; a different form is refused with `pronto-unsupported-form`.
119872
- */
119873
- function decodePronto(code) {
119874
- const words = code.trim().split(/\s+/).filter((w) => w.length > 0);
119875
- if (words.length < 4) throw new IrCodecError("pronto-malformed", `Pronto code needs at least 4 words, got ${words.length}`);
119876
- const values = words.map((w) => /^[0-9a-fA-F]{1,4}$/.test(w) ? parseInt(w, 16) : NaN);
119877
- if (values.some((v) => Number.isNaN(v))) throw new IrCodecError("pronto-malformed", "Pronto code must be 4-digit hexadecimal words");
119878
- const [form, freqWord, introCount, repeatCount] = values;
119879
- if (form === void 0 || freqWord === void 0 || introCount === void 0 || repeatCount === void 0) throw new IrCodecError("pronto-malformed", "Pronto header incomplete");
119880
- if (form !== LEARNED_FORM) throw new IrCodecError("pronto-unsupported-form", `Pronto form ${words[0] ?? ""} is not supported, only the learned 0000 form`);
119881
- if (freqWord === 0) throw new IrCodecError("pronto-malformed", "Pronto frequency word is zero");
119882
- const pairs = introCount + repeatCount;
119883
- if (pairs === 0 || values.length !== 4 + pairs * 2) throw new IrCodecError("pronto-malformed", `Pronto declares ${pairs} burst pairs but carries ${(values.length - 4) / 2} word pairs`);
119884
- const unitUs = freqWord * PRONTO_CLOCK_US;
119885
- const timings = values.slice(4).map((count, index) => {
119886
- const us = Math.max(1, Math.round(count * unitUs));
119887
- return index % 2 === 0 ? us : -us;
119888
- });
119889
- const checked = IrTimingsSchema.safeParse(timings);
119890
- if (!checked.success) throw new IrCodecError("invalid-timings", `Pronto decoded to invalid timings: ${checked.error.message}`);
119891
- return {
119892
- timings: checked.data,
119893
- carrierHz: Math.round(1e6 / unitUs)
119894
- };
119895
- }
119896
- //#endregion
119897
- //#region src/ir/ir-payload.ts
119898
- /** What a remote command stores: a raw pulse train, or a learned Pronto code. */
119899
- var IrPayloadSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
119900
- kind: zod.z.literal("raw"),
119901
- timings: zod.z.array(zod.z.number().int()),
119902
- settings: TransmitSettingsSchema.optional()
119903
- }), zod.z.object({
119904
- kind: zod.z.literal("pronto"),
119905
- code: zod.z.string()
119906
- })]);
119907
- /** Resolve a payload to what `ir-transmitter.send` takes. Throws `IrCodecError`. */
119908
- function toTimings(payload) {
119909
- if (payload.kind === "pronto") {
119910
- const decoded = decodePronto(payload.code);
119911
- return {
119912
- timings: decoded.timings,
119913
- settings: { carrierHz: decoded.carrierHz }
119914
- };
119915
- }
119916
- const checked = IrTimingsSchema.safeParse(payload.timings);
119917
- if (!checked.success) throw new IrCodecError("invalid-timings", checked.error.message);
119918
- return {
119919
- timings: checked.data,
119920
- settings: payload.settings ?? {}
119921
- };
119922
- }
119923
- //#endregion
119924
120666
  //#region src/ir/smartir.ts
119925
120667
  /** SmartIR's own default pause between two commands, in seconds. */
119926
120668
  var DEFAULT_DELAY_S = .5;
@@ -120660,6 +121402,8 @@ exports.EMBEDDING_MODEL_FAILED = EMBEDDING_MODEL_FAILED;
120660
121402
  exports.EMBEDDING_REQUIREMENTS_FAILED = EMBEDDING_REQUIREMENTS_FAILED;
120661
121403
  exports.EMBEDDING_REQUIREMENTS_UNAVAILABLE = EMBEDDING_REQUIREMENTS_UNAVAILABLE;
120662
121404
  exports.EMPTY_AUTOMATION_STATE = EMPTY_AUTOMATION_STATE;
121405
+ exports.EMPTY_REMOTE_ASSUMED_STATE = EMPTY_REMOTE_ASSUMED_STATE;
121406
+ exports.EMPTY_REMOTE_CONFIG = EMPTY_REMOTE_CONFIG;
120663
121407
  exports.ESTIMATE_COMPUTE_RATIO_MAX = ESTIMATE_COMPUTE_RATIO_MAX;
120664
121408
  exports.ESTIMATE_COMPUTE_RATIO_MIN = ESTIMATE_COMPUTE_RATIO_MIN;
120665
121409
  exports.EVENTFUL_CAP_NAMES = EVENTFUL_CAP_NAMES;
@@ -121025,7 +121769,10 @@ exports.MAX_EXPRESSION_EVAL_STEPS = require_composition.MAX_EXPRESSION_EVAL_STEP
121025
121769
  exports.MAX_EXPRESSION_SOURCE_LENGTH = require_composition.MAX_EXPRESSION_SOURCE_LENGTH;
121026
121770
  exports.MAX_HELPER_MEMBERS = require_composition.MAX_HELPER_MEMBERS;
121027
121771
  exports.MAX_KEYS = MAX_KEYS;
121772
+ exports.MAX_LAYOUT_SPAN = MAX_LAYOUT_SPAN;
121028
121773
  exports.MAX_REASONS_PER_KEY = MAX_REASONS_PER_KEY;
121774
+ exports.MAX_REMOTE_BINDINGS = MAX_REMOTE_BINDINGS;
121775
+ exports.MAX_REMOTE_CUSTOM_BUTTONS = MAX_REMOTE_CUSTOM_BUTTONS;
121029
121776
  exports.MAX_REQUEUED_BUCKETS_PER_SERIES = MAX_REQUEUED_BUCKETS_PER_SERIES;
121030
121777
  exports.MAX_SENSOR_TRIGGER_DEVICES = MAX_SENSOR_TRIGGER_DEVICES;
121031
121778
  exports.MAX_TRACE_KEEP = MAX_TRACE_KEEP;
@@ -121069,8 +121816,10 @@ exports.MediaFileInfoSchema = MediaFileInfoSchema;
121069
121816
  exports.MediaFileKindEnum = MediaFileKindEnum;
121070
121817
  exports.MediaFileRefSchema = MediaFileRefSchema;
121071
121818
  exports.MediaFileSchema = MediaFileSchema;
121819
+ exports.MediaPlayerAssumedFieldSchema = MediaPlayerAssumedFieldSchema;
121072
121820
  exports.MediaPlayerRepeatSchema = MediaPlayerRepeatSchema;
121073
121821
  exports.MediaPlayerStateSchema = MediaPlayerStateSchema;
121822
+ exports.MediaPlayerStateSourceSchema = MediaPlayerStateSourceSchema;
121074
121823
  exports.MediaPlayerStatusSchema = MediaPlayerStatusSchema;
121075
121824
  exports.MediaPresenceOwnerKindSchema = MediaPresenceOwnerKindSchema;
121076
121825
  exports.MediaRelocateModeSchema = MediaRelocateModeSchema;
@@ -121583,6 +122332,13 @@ exports.RECOVERY_VIEW_PX = RECOVERY_VIEW_PX;
121583
122332
  exports.REDACTED_SECRET = require_sleep.REDACTED_SECRET;
121584
122333
  exports.REFERENCE_COMPUTE = REFERENCE_COMPUTE;
121585
122334
  exports.REFERENCE_MATCH_FRACTION = REFERENCE_MATCH_FRACTION;
122335
+ exports.REMOTE_CONFIG_REFUSALS = REMOTE_CONFIG_REFUSALS;
122336
+ exports.REMOTE_CUSTOM_ROLE_PREFIX = REMOTE_CUSTOM_ROLE_PREFIX;
122337
+ exports.REMOTE_KNOWN_ROLES = REMOTE_KNOWN_ROLES;
122338
+ exports.REMOTE_MAX_REPEAT = REMOTE_MAX_REPEAT;
122339
+ exports.REMOTE_MEDIA_COMMANDS = REMOTE_MEDIA_COMMANDS;
122340
+ exports.REMOTE_POWER_MODES = REMOTE_POWER_MODES;
122341
+ exports.REMOTE_REFUSAL_REASONS = REMOTE_REFUSAL_REASONS;
121586
122342
  exports.REMOVAL_RECONCILE_INTERVAL_MS = REMOVAL_RECONCILE_INTERVAL_MS;
121587
122343
  exports.REMOVED_MODEL_IDS = REMOVED_MODEL_IDS;
121588
122344
  exports.REMOVE_LEAF = require_sleep.REMOVE_LEAF;
@@ -121590,6 +122346,7 @@ exports.RESERVED_BINDING_NAMES = require_composition.RESERVED_BINDING_NAMES;
121590
122346
  exports.RESERVED_COLLECTION_SOURCES = RESERVED_COLLECTION_SOURCES;
121591
122347
  exports.RESET_PERIODS = require_composition.RESET_PERIODS;
121592
122348
  exports.RESTORED_CAP_NAMES = RESTORED_CAP_NAMES;
122349
+ exports.RESTREAM_INPUT_PROBE = require_composition.RESTREAM_INPUT_PROBE;
121593
122350
  exports.RETRAIN_REVIEW_ADJUSTED_IOU = RETRAIN_REVIEW_ADJUSTED_IOU;
121594
122351
  exports.RETRAIN_REVIEW_MATCH_IOU = RETRAIN_REVIEW_MATCH_IOU;
121595
122352
  exports.RETRAIN_REVIEW_STATUSES = RETRAIN_REVIEW_STATUSES;
@@ -121691,6 +122448,22 @@ exports.RelocateJobStateSchema = RelocateJobStateSchema;
121691
122448
  exports.RelocateMediaInputSchema = RelocateMediaInputSchema;
121692
122449
  exports.RelocateResidueInputSchema = RelocateResidueInputSchema;
121693
122450
  exports.RelocateResidueSchema = RelocateResidueSchema;
122451
+ exports.RemoteAssumedStateSchema = RemoteAssumedStateSchema;
122452
+ exports.RemoteBindingActionSchema = RemoteBindingActionSchema;
122453
+ exports.RemoteBindingSchema = RemoteBindingSchema;
122454
+ exports.RemoteBoundCommandSchema = RemoteBoundCommandSchema;
122455
+ exports.RemoteCommandListSchema = RemoteCommandListSchema;
122456
+ exports.RemoteConfigSchema = RemoteConfigSchema;
122457
+ exports.RemoteCustomButtonSchema = RemoteCustomButtonSchema;
122458
+ exports.RemoteLayoutButtonSchema = RemoteLayoutButtonSchema;
122459
+ exports.RemoteLayoutPayloadSchema = RemoteLayoutPayloadSchema;
122460
+ exports.RemoteMediaCommandResultSchema = RemoteMediaCommandResultSchema;
122461
+ exports.RemoteMediaCommandSchema = RemoteMediaCommandSchema;
122462
+ exports.RemotePowerModeSchema = RemotePowerModeSchema;
122463
+ exports.RemoteRefusalReasonSchema = RemoteRefusalReasonSchema;
122464
+ exports.RemoteRoleSchema = RemoteRoleSchema;
122465
+ exports.RemoteSendResultSchema = RemoteSendResultSchema;
122466
+ exports.RemoteSetConfigResultSchema = RemoteSetConfigResultSchema;
121694
122467
  exports.RenderedAsSchema = RenderedAsSchema;
121695
122468
  exports.RenewDeviceCertificateOutputSchema = RenewDeviceCertificateOutputSchema;
121696
122469
  exports.RenewDeviceCertificateResultSchema = RenewDeviceCertificateResultSchema;
@@ -121946,6 +122719,9 @@ exports.SwitchStatusSchema = SwitchStatusSchema;
121946
122719
  exports.SystemHeldSecretNameSchema = SystemHeldSecretNameSchema;
121947
122720
  exports.SystemMetricsSchema = SystemMetricsSchema;
121948
122721
  exports.SystemMirror = SystemMirror;
122722
+ exports.TEMPLATE_KINDS = TEMPLATE_KINDS;
122723
+ exports.TEMPLATE_PAYLOAD_SCHEMAS = TEMPLATE_PAYLOAD_SCHEMAS;
122724
+ exports.TEMPLATE_WRITE_REFUSALS = TEMPLATE_WRITE_REFUSALS;
121949
122725
  exports.TIMELAPSE_DENSE_FLOOR_SEC = TIMELAPSE_DENSE_FLOOR_SEC;
121950
122726
  exports.TIMEZONES = TIMEZONES;
121951
122727
  exports.TRACK_RELATIONS_DEFAULT_LIMIT = TRACK_RELATIONS_DEFAULT_LIMIT;
@@ -121959,6 +122735,11 @@ exports.TargetKindLevelSchema = TargetKindLevelSchema;
121959
122735
  exports.TargetKindSchema = TargetKindSchema;
121960
122736
  exports.TargetSchema = TargetSchema;
121961
122737
  exports.TemperatureSensorStatusSchema = TemperatureSensorStatusSchema;
122738
+ exports.TemplateDeleteResultSchema = TemplateDeleteResultSchema;
122739
+ exports.TemplateKindSchema = TemplateKindSchema;
122740
+ exports.TemplateRecordSchema = TemplateRecordSchema;
122741
+ exports.TemplateUserSchema = TemplateUserSchema;
122742
+ exports.TemplateWriteResultSchema = TemplateWriteResultSchema;
121962
122743
  exports.TerminalInstanceInfoSchema = TerminalInstanceInfoSchema;
121963
122744
  exports.TerminalLegacyCameraSchema = TerminalLegacyCameraSchema;
121964
122745
  exports.TerminalOutputBatchSchema = TerminalOutputBatchSchema;
@@ -122266,6 +123047,7 @@ exports.capacitySessionsOf = capacitySessionsOf;
122266
123047
  exports.carbonMonoxideCapability = carbonMonoxideCapability;
122267
123048
  exports.cellsToRects = cellsToRects;
122268
123049
  exports.checkRoleAddress = checkRoleAddress;
123050
+ exports.checkTemplatePayload = checkTemplatePayload;
122269
123051
  exports.claimedRequestOrigins = claimedRequestOrigins;
122270
123052
  exports.classifyBearerPrincipal = classifyBearerPrincipal;
122271
123053
  exports.classifyCamerasForBenchmark = classifyCamerasForBenchmark;
@@ -122369,7 +123151,9 @@ exports.createSliceHandle = require_sleep.createSliceHandle;
122369
123151
  exports.createStatefulExpressionMemory = require_composition.createStatefulExpressionMemory;
122370
123152
  exports.createSystemProxy = createSystemProxy;
122371
123153
  exports.customAction = require_sleep.customAction;
123154
+ exports.customKeyOfRole = customKeyOfRole;
122372
123155
  exports.customModelRegistryCapability = customModelRegistryCapability;
123156
+ exports.customRole = customRole;
122373
123157
  exports.dataStoreProviderCapability = dataStoreProviderCapability;
122374
123158
  exports.databaseSnapshotCapability = databaseSnapshotCapability;
122375
123159
  exports.dayNightCapability = dayNightCapability;
@@ -122648,6 +123432,9 @@ exports.isOccupancyRule = isOccupancyRule;
122648
123432
  exports.isOwnerOnlyEventCategory = require_event_category.isOwnerOnlyEventCategory;
122649
123433
  exports.isPlugApplianceType = isPlugApplianceType;
122650
123434
  exports.isReadingQuantity = require_sleep.isReadingQuantity;
123435
+ exports.isRemoteCustomKey = isRemoteCustomKey;
123436
+ exports.isRemoteKnownRole = isRemoteKnownRole;
123437
+ exports.isRemoteRole = isRemoteRole;
122651
123438
  exports.isRemovedModelId = isRemovedModelId;
122652
123439
  exports.isRestoredCap = isRestoredCap;
122653
123440
  exports.isRetiredClearKey = isRetiredClearKey;
@@ -122673,6 +123460,7 @@ exports.isSystemEventFamily = isSystemEventFamily;
122673
123460
  exports.isSystemEventRecoveryKind = isSystemEventRecoveryKind;
122674
123461
  exports.isSystemEventSummaryRule = isSystemEventSummaryRule;
122675
123462
  exports.isSystemRuleKind = isSystemRuleKind;
123463
+ exports.isTemplateKind = isTemplateKind;
122676
123464
  exports.isUnfilledRequiredProblem = isUnfilledRequiredProblem;
122677
123465
  exports.isVectorIndexDimConflict = isVectorIndexDimConflict;
122678
123466
  exports.isVoidInput = isVoidInput;
@@ -122932,6 +123720,7 @@ exports.reducePoints = reducePoints;
122932
123720
  exports.reducerForActive = reducerForActive;
122933
123721
  exports.registerKnownSecret = require_sleep.registerKnownSecret;
122934
123722
  exports.remapCropDetections = remapCropDetections;
123723
+ exports.remoteCapability = remoteCapability;
122935
123724
  exports.removeScalarTarget = removeScalarTarget;
122936
123725
  exports.renameAlarmProfile = renameAlarmProfile;
122937
123726
  exports.renderVocabPhrase = renderVocabPhrase;
@@ -123082,6 +123871,7 @@ exports.sleep = require_sleep.sleep;
123082
123871
  exports.sleepCancellable = require_sleep.sleepCancellable;
123083
123872
  exports.sliceActiveValue = require_composition.sliceActiveValue;
123084
123873
  exports.sliceChangedAt = require_composition.sliceChangedAt;
123874
+ exports.smartIrDraft = smartIrDraft;
123085
123875
  exports.smokeCapability = smokeCapability;
123086
123876
  exports.smtpProviderCapability = smtpProviderCapability;
123087
123877
  exports.snapshotCapability = snapshotCapability;
@@ -123153,6 +123943,7 @@ exports.taskLogEntrySchema = taskLogEntrySchema;
123153
123943
  exports.taskPhaseSchema = taskPhaseSchema;
123154
123944
  exports.taskTargetSchema = taskTargetSchema;
123155
123945
  exports.temperatureSensorCapability = temperatureSensorCapability;
123946
+ exports.templateCapability = templateCapability;
123156
123947
  exports.templateCarriesSummary = templateCarriesSummary;
123157
123948
  exports.templateDeviceType = templateDeviceType;
123158
123949
  exports.templateVarDeliveryApplies = templateVarDeliveryApplies;