@camstack/types 1.2.439 → 1.2.441

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 (34) hide show
  1. package/CHANGELOG.md +17 -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/hub-address.d.ts +2 -0
  7. package/dist/capabilities/index.d.ts +6 -2
  8. package/dist/capabilities/media-player.cap.d.ts +50 -0
  9. package/dist/capabilities/pet-feeder.cap.d.ts +9 -0
  10. package/dist/capabilities/remote.cap.d.ts +264 -0
  11. package/dist/capabilities/template.cap.d.ts +161 -0
  12. package/dist/client.js +14 -0
  13. package/dist/client.mjs +14 -0
  14. package/dist/generated/addon-api.d.ts +90 -0
  15. package/dist/generated/addon-cap-facts.d.ts +1 -1
  16. package/dist/generated/cap-input-defaults.d.ts +1 -1
  17. package/dist/generated/capability-router-map.d.ts +10 -4
  18. package/dist/generated/collection-array-methods.d.ts +1 -1
  19. package/dist/generated/customize-catalog.d.ts +18 -1
  20. package/dist/generated/method-access-map.d.ts +1 -1
  21. package/dist/generated/method-device-selectors.d.ts +2 -2
  22. package/dist/generated/system-proxy.d.ts +4 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.js +863 -71
  25. package/dist/index.mjs +816 -72
  26. package/dist/interfaces/device-call-redirect.d.ts +42 -0
  27. package/dist/interfaces/kernel-abstractions.d.ts +12 -0
  28. package/dist/remote/remote-config.d.ts +116 -0
  29. package/dist/remote/remote-roles.d.ts +21 -0
  30. package/dist/remote/smartir-draft.d.ts +25 -0
  31. package/dist/{sleep-g6ZjBHBL.js → sleep-BpwNJ1Su.js} +19 -0
  32. package/dist/{sleep-BAvUCi_2.mjs → sleep-DJws6ho7.mjs} +19 -0
  33. package/dist/template/template-kinds.d.ts +26 -0
  34. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
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
4
  const require_composition = require("./composition-DCmtFE-X.js");
5
5
  const require_node_color = require("./node-color.js");
@@ -44110,6 +44110,8 @@ var AGENT_MDNS_QUERY_NAME = "_camstack-agent._tcp.local";
44110
44110
  var AGENT_MDNS_TXT_KEYS = {
44111
44111
  nodeId: "nodeId",
44112
44112
  version: "version",
44113
+ /** The agent's display name; absent on an older agent. */
44114
+ name: "name",
44113
44115
  /** `unpaired` | `pending` | `paired`. */
44114
44116
  state: "state",
44115
44117
  /** First 16 hex chars of the SHA-256 of the agent's mesh leaf; present once paired. */
@@ -65197,6 +65199,456 @@ onBrightnessChanged: { data: zod.z.object({
65197
65199
  } } }
65198
65200
  };
65199
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
65200
65652
  //#region src/capabilities/button.cap.ts
65201
65653
  /**
65202
65654
  * button — device-scoped capability for HA `button.*` / `input_button.*`
@@ -69995,9 +70447,35 @@ var MediaInfoSchema = zod.z.object({
69995
70447
  /** Optional cover-art / thumbnail URL. */
69996
70448
  imageUrl: zod.z.string().optional()
69997
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
+ ]);
69998
70465
  var MediaPlayerStatusSchema = zod.z.object({
69999
70466
  /** Playback lifecycle state. */
70000
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([]),
70001
70479
  /** Volume as 0..100 inclusive. Null when the device has no volume
70002
70480
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
70003
70481
  volumeLevel: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
@@ -72481,6 +72959,14 @@ var PetFeederStatusSchema = zod.z.object({
72481
72959
  feedSound: zod.z.boolean(),
72482
72960
  /** Speaker / prompt volume level (device-scaled integer). */
72483
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(),
72484
72970
  /** Ms epoch when the slice was last refreshed from the cloud. */
72485
72971
  lastFetchedAt: zod.z.number()
72486
72972
  });
@@ -81090,6 +81576,186 @@ var PIPELINE_OWNER_CAPABILITY_NAMES = [
81090
81576
  "pipeline-runner"
81091
81577
  ];
81092
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
81093
81759
  //#region src/running-state/from-operation.ts
81094
81760
  function programStartFromRemaining(running, remainingSeconds, fetchedAt) {
81095
81761
  if (!running || remainingSeconds === null || remainingSeconds === void 0) return null;
@@ -82713,7 +83379,7 @@ function convertUnitTargets(from) {
82713
83379
  //#region src/generated/customize-catalog.ts
82714
83380
  /**
82715
83381
  * Customize catalog, projected from every `*.cap.ts` `customize` block.
82716
- * 6 caps. Field keys are `runtimeState` paths. No Zod.
83382
+ * 7 caps. Field keys are `runtimeState` paths. No Zod.
82717
83383
  */
82718
83384
  var CAP_CUSTOMIZE_CATALOG = {
82719
83385
  "battery": {
@@ -83140,6 +83806,21 @@ var CAP_CUSTOMIZE_CATALOG = {
83140
83806
  }
83141
83807
  ]
83142
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
+ },
83143
83824
  "running-state": {
83144
83825
  version: 1,
83145
83826
  targets: [{
@@ -95090,6 +95771,41 @@ var METHOD_ACCESS_MAP = Object.freeze({
95090
95771
  access: "create",
95091
95772
  role: "admin"
95092
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
+ },
95093
95809
  "runningState.getStatus": {
95094
95810
  capName: "running-state",
95095
95811
  capScope: "device",
@@ -96280,6 +96996,41 @@ var METHOD_ACCESS_MAP = Object.freeze({
96280
96996
  access: "view",
96281
96997
  role: "viewer"
96282
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
+ },
96283
97034
  "terminalSession.adoptLegacyMonitor": {
96284
97035
  capName: "terminal-session",
96285
97036
  capScope: "system",
@@ -97394,6 +98145,7 @@ var KNOWN_CAP_NAMES = [
97394
98145
  "recording-onboard",
97395
98146
  "recording-signal",
97396
98147
  "records",
98148
+ "remote",
97397
98149
  "running-state",
97398
98150
  "scene-monitor",
97399
98151
  "script-runner",
@@ -97417,6 +98169,7 @@ var KNOWN_CAP_NAMES = [
97417
98169
  "system",
97418
98170
  "tamper",
97419
98171
  "temperature-sensor",
98172
+ "template",
97420
98173
  "terminal-session",
97421
98174
  "toast",
97422
98175
  "training",
@@ -97610,6 +98363,7 @@ var SYSTEM_CAP_NAMES = [
97610
98363
  "recording-archive",
97611
98364
  "recording-export",
97612
98365
  "records",
98366
+ "remote",
97613
98367
  "server-management",
97614
98368
  "settings-store",
97615
98369
  "smtp-provider",
@@ -97622,6 +98376,7 @@ var SYSTEM_CAP_NAMES = [
97622
98376
  "storage-provider",
97623
98377
  "stream-broker",
97624
98378
  "system",
98379
+ "template",
97625
98380
  "terminal-session",
97626
98381
  "toast",
97627
98382
  "training",
@@ -101508,6 +102263,7 @@ var SYSTEM_CAP_VOCABULARY = {
101508
102263
  "recording-archive": system(STORAGE),
101509
102264
  "recording-export": system("recording exports — requested from the timeline, not an automation step"),
101510
102265
  records: system(INTERNAL),
102266
+ remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
101511
102267
  "server-management": system(CLUSTER),
101512
102268
  "settings-store": system(INTERNAL),
101513
102269
  "smtp-provider": system(INTERNAL),
@@ -101520,6 +102276,7 @@ var SYSTEM_CAP_VOCABULARY = {
101520
102276
  "storage-provider": system(STORAGE),
101521
102277
  "stream-broker": system(PIPELINE),
101522
102278
  system: system(CLUSTER),
102279
+ template: system("operator-authored templates — edited in their own editor, never actuated"),
101523
102280
  "terminal-session": system(CLUSTER),
101524
102281
  toast: system(UI_PLUMBING),
101525
102282
  training: system(ML),
@@ -103489,6 +104246,7 @@ var CAPABILITY_NAMES = {
103489
104246
  recordingOnboard: "recording-onboard",
103490
104247
  recordingSignal: "recording-signal",
103491
104248
  records: "records",
104249
+ remote: "remote",
103492
104250
  runningState: "running-state",
103493
104251
  sceneMonitor: "scene-monitor",
103494
104252
  scriptRunner: "script-runner",
@@ -103512,6 +104270,7 @@ var CAPABILITY_NAMES = {
103512
104270
  system: "system",
103513
104271
  tamper: "tamper",
103514
104272
  temperatureSensor: "temperature-sensor",
104273
+ template: "template",
103515
104274
  terminalSession: "terminal-session",
103516
104275
  toast: "toast",
103517
104276
  training: "training",
@@ -104115,6 +104874,10 @@ var CAPABILITY_ROUTER_KEYS = [
104115
104874
  key: "records",
104116
104875
  name: "records"
104117
104876
  },
104877
+ {
104878
+ key: "remote",
104879
+ name: "remote"
104880
+ },
104118
104881
  {
104119
104882
  key: "runningState",
104120
104883
  name: "running-state"
@@ -104207,6 +104970,10 @@ var CAPABILITY_ROUTER_KEYS = [
104207
104970
  key: "temperatureSensor",
104208
104971
  name: "temperature-sensor"
104209
104972
  },
104973
+ {
104974
+ key: "template",
104975
+ name: "template"
104976
+ },
104210
104977
  {
104211
104978
  key: "terminalSession",
104212
104979
  name: "terminal-session"
@@ -104443,6 +105210,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
104443
105210
  recordingOnboardCapability,
104444
105211
  recordingSignalCapability,
104445
105212
  recordsCapability,
105213
+ remoteCapability,
104446
105214
  runningStateCapability,
104447
105215
  sceneMonitorCapability,
104448
105216
  scriptRunnerCapability,
@@ -104466,6 +105234,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
104466
105234
  systemCapability,
104467
105235
  tamperCapability,
104468
105236
  temperatureSensorCapability,
105237
+ templateCapability,
104469
105238
  terminalSessionCapability,
104470
105239
  toastCapability,
104471
105240
  trainingCapability,
@@ -106815,6 +107584,31 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
106815
107584
  form: "single",
106816
107585
  optional: true
106817
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
+ }],
106818
107612
  "runningState.getStatus": [{
106819
107613
  name: "deviceId",
106820
107614
  form: "single",
@@ -107472,6 +108266,11 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
107472
108266
  "records.histogram",
107473
108267
  "records.putMany",
107474
108268
  "records.query",
108269
+ "remote.getConfig",
108270
+ "remote.listCommands",
108271
+ "remote.sendCommand",
108272
+ "remote.sendMediaCommand",
108273
+ "remote.setConfig",
107475
108274
  "storageAdmin.readRecords",
107476
108275
  "streamBroker.acquireEgressTranscode",
107477
108276
  "streamBroker.applyDeviceSettingsPatch",
@@ -109157,6 +109956,13 @@ function createSystemProxy(api) {
109157
109956
  getDownloadUrl: (input) => dispatch("recordingExport", "getDownloadUrl", "query", input),
109158
109957
  readExportBytes: (input) => dispatch("recordingExport", "readExportBytes", "query", input)
109159
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
+ },
109160
109966
  serverManagement: {
109161
109967
  getServerPackageStatus: (input) => dispatch("serverManagement", "getServerPackageStatus", "query", input),
109162
109968
  checkServerUpdate: (input) => dispatch("serverManagement", "checkServerUpdate", "mutation", input),
@@ -109264,6 +110070,13 @@ function createSystemProxy(api) {
109264
110070
  getLoggingSettings: (input) => dispatch("system", "getLoggingSettings", "query", input),
109265
110071
  setLoggingSettings: (input) => dispatch("system", "setLoggingSettings", "mutation", input)
109266
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
+ },
109267
110080
  terminalSession: {
109268
110081
  listProfiles: (input) => dispatch("terminalSession", "listProfiles", "query", input),
109269
110082
  listInstances: (input) => dispatch("terminalSession", "listInstances", "query", input),
@@ -119850,75 +120663,6 @@ function armRemovalReconcile(opts) {
119850
120663
  return () => clearInterval(timer);
119851
120664
  }
119852
120665
  //#endregion
119853
- //#region src/ir/ir-error.ts
119854
- var IrCodecError = class extends Error {
119855
- code;
119856
- constructor(code, message) {
119857
- super(message);
119858
- this.name = "IrCodecError";
119859
- this.code = code;
119860
- }
119861
- };
119862
- //#endregion
119863
- //#region src/ir/pronto.ts
119864
- /** One Pronto frequency-word unit, in microseconds, per unit of the word. */
119865
- var PRONTO_CLOCK_US = .241246;
119866
- var LEARNED_FORM = 0;
119867
- /**
119868
- * Decode a learned (0000 form) Pronto hex string. The intro burst is followed by
119869
- * the repeat burst once; a different form is refused with `pronto-unsupported-form`.
119870
- */
119871
- function decodePronto(code) {
119872
- const words = code.trim().split(/\s+/).filter((w) => w.length > 0);
119873
- if (words.length < 4) throw new IrCodecError("pronto-malformed", `Pronto code needs at least 4 words, got ${words.length}`);
119874
- const values = words.map((w) => /^[0-9a-fA-F]{1,4}$/.test(w) ? parseInt(w, 16) : NaN);
119875
- if (values.some((v) => Number.isNaN(v))) throw new IrCodecError("pronto-malformed", "Pronto code must be 4-digit hexadecimal words");
119876
- const [form, freqWord, introCount, repeatCount] = values;
119877
- if (form === void 0 || freqWord === void 0 || introCount === void 0 || repeatCount === void 0) throw new IrCodecError("pronto-malformed", "Pronto header incomplete");
119878
- if (form !== LEARNED_FORM) throw new IrCodecError("pronto-unsupported-form", `Pronto form ${words[0] ?? ""} is not supported, only the learned 0000 form`);
119879
- if (freqWord === 0) throw new IrCodecError("pronto-malformed", "Pronto frequency word is zero");
119880
- const pairs = introCount + repeatCount;
119881
- 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`);
119882
- const unitUs = freqWord * PRONTO_CLOCK_US;
119883
- const timings = values.slice(4).map((count, index) => {
119884
- const us = Math.max(1, Math.round(count * unitUs));
119885
- return index % 2 === 0 ? us : -us;
119886
- });
119887
- const checked = IrTimingsSchema.safeParse(timings);
119888
- if (!checked.success) throw new IrCodecError("invalid-timings", `Pronto decoded to invalid timings: ${checked.error.message}`);
119889
- return {
119890
- timings: checked.data,
119891
- carrierHz: Math.round(1e6 / unitUs)
119892
- };
119893
- }
119894
- //#endregion
119895
- //#region src/ir/ir-payload.ts
119896
- /** What a remote command stores: a raw pulse train, or a learned Pronto code. */
119897
- var IrPayloadSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
119898
- kind: zod.z.literal("raw"),
119899
- timings: zod.z.array(zod.z.number().int()),
119900
- settings: TransmitSettingsSchema.optional()
119901
- }), zod.z.object({
119902
- kind: zod.z.literal("pronto"),
119903
- code: zod.z.string()
119904
- })]);
119905
- /** Resolve a payload to what `ir-transmitter.send` takes. Throws `IrCodecError`. */
119906
- function toTimings(payload) {
119907
- if (payload.kind === "pronto") {
119908
- const decoded = decodePronto(payload.code);
119909
- return {
119910
- timings: decoded.timings,
119911
- settings: { carrierHz: decoded.carrierHz }
119912
- };
119913
- }
119914
- const checked = IrTimingsSchema.safeParse(payload.timings);
119915
- if (!checked.success) throw new IrCodecError("invalid-timings", checked.error.message);
119916
- return {
119917
- timings: checked.data,
119918
- settings: payload.settings ?? {}
119919
- };
119920
- }
119921
- //#endregion
119922
120666
  //#region src/ir/smartir.ts
119923
120667
  /** SmartIR's own default pause between two commands, in seconds. */
119924
120668
  var DEFAULT_DELAY_S = .5;
@@ -120658,6 +121402,8 @@ exports.EMBEDDING_MODEL_FAILED = EMBEDDING_MODEL_FAILED;
120658
121402
  exports.EMBEDDING_REQUIREMENTS_FAILED = EMBEDDING_REQUIREMENTS_FAILED;
120659
121403
  exports.EMBEDDING_REQUIREMENTS_UNAVAILABLE = EMBEDDING_REQUIREMENTS_UNAVAILABLE;
120660
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;
120661
121407
  exports.ESTIMATE_COMPUTE_RATIO_MAX = ESTIMATE_COMPUTE_RATIO_MAX;
120662
121408
  exports.ESTIMATE_COMPUTE_RATIO_MIN = ESTIMATE_COMPUTE_RATIO_MIN;
120663
121409
  exports.EVENTFUL_CAP_NAMES = EVENTFUL_CAP_NAMES;
@@ -121023,7 +121769,10 @@ exports.MAX_EXPRESSION_EVAL_STEPS = require_composition.MAX_EXPRESSION_EVAL_STEP
121023
121769
  exports.MAX_EXPRESSION_SOURCE_LENGTH = require_composition.MAX_EXPRESSION_SOURCE_LENGTH;
121024
121770
  exports.MAX_HELPER_MEMBERS = require_composition.MAX_HELPER_MEMBERS;
121025
121771
  exports.MAX_KEYS = MAX_KEYS;
121772
+ exports.MAX_LAYOUT_SPAN = MAX_LAYOUT_SPAN;
121026
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;
121027
121776
  exports.MAX_REQUEUED_BUCKETS_PER_SERIES = MAX_REQUEUED_BUCKETS_PER_SERIES;
121028
121777
  exports.MAX_SENSOR_TRIGGER_DEVICES = MAX_SENSOR_TRIGGER_DEVICES;
121029
121778
  exports.MAX_TRACE_KEEP = MAX_TRACE_KEEP;
@@ -121067,8 +121816,10 @@ exports.MediaFileInfoSchema = MediaFileInfoSchema;
121067
121816
  exports.MediaFileKindEnum = MediaFileKindEnum;
121068
121817
  exports.MediaFileRefSchema = MediaFileRefSchema;
121069
121818
  exports.MediaFileSchema = MediaFileSchema;
121819
+ exports.MediaPlayerAssumedFieldSchema = MediaPlayerAssumedFieldSchema;
121070
121820
  exports.MediaPlayerRepeatSchema = MediaPlayerRepeatSchema;
121071
121821
  exports.MediaPlayerStateSchema = MediaPlayerStateSchema;
121822
+ exports.MediaPlayerStateSourceSchema = MediaPlayerStateSourceSchema;
121072
121823
  exports.MediaPlayerStatusSchema = MediaPlayerStatusSchema;
121073
121824
  exports.MediaPresenceOwnerKindSchema = MediaPresenceOwnerKindSchema;
121074
121825
  exports.MediaRelocateModeSchema = MediaRelocateModeSchema;
@@ -121581,6 +122332,13 @@ exports.RECOVERY_VIEW_PX = RECOVERY_VIEW_PX;
121581
122332
  exports.REDACTED_SECRET = require_sleep.REDACTED_SECRET;
121582
122333
  exports.REFERENCE_COMPUTE = REFERENCE_COMPUTE;
121583
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;
121584
122342
  exports.REMOVAL_RECONCILE_INTERVAL_MS = REMOVAL_RECONCILE_INTERVAL_MS;
121585
122343
  exports.REMOVED_MODEL_IDS = REMOVED_MODEL_IDS;
121586
122344
  exports.REMOVE_LEAF = require_sleep.REMOVE_LEAF;
@@ -121689,6 +122447,22 @@ exports.RelocateJobStateSchema = RelocateJobStateSchema;
121689
122447
  exports.RelocateMediaInputSchema = RelocateMediaInputSchema;
121690
122448
  exports.RelocateResidueInputSchema = RelocateResidueInputSchema;
121691
122449
  exports.RelocateResidueSchema = RelocateResidueSchema;
122450
+ exports.RemoteAssumedStateSchema = RemoteAssumedStateSchema;
122451
+ exports.RemoteBindingActionSchema = RemoteBindingActionSchema;
122452
+ exports.RemoteBindingSchema = RemoteBindingSchema;
122453
+ exports.RemoteBoundCommandSchema = RemoteBoundCommandSchema;
122454
+ exports.RemoteCommandListSchema = RemoteCommandListSchema;
122455
+ exports.RemoteConfigSchema = RemoteConfigSchema;
122456
+ exports.RemoteCustomButtonSchema = RemoteCustomButtonSchema;
122457
+ exports.RemoteLayoutButtonSchema = RemoteLayoutButtonSchema;
122458
+ exports.RemoteLayoutPayloadSchema = RemoteLayoutPayloadSchema;
122459
+ exports.RemoteMediaCommandResultSchema = RemoteMediaCommandResultSchema;
122460
+ exports.RemoteMediaCommandSchema = RemoteMediaCommandSchema;
122461
+ exports.RemotePowerModeSchema = RemotePowerModeSchema;
122462
+ exports.RemoteRefusalReasonSchema = RemoteRefusalReasonSchema;
122463
+ exports.RemoteRoleSchema = RemoteRoleSchema;
122464
+ exports.RemoteSendResultSchema = RemoteSendResultSchema;
122465
+ exports.RemoteSetConfigResultSchema = RemoteSetConfigResultSchema;
121692
122466
  exports.RenderedAsSchema = RenderedAsSchema;
121693
122467
  exports.RenewDeviceCertificateOutputSchema = RenewDeviceCertificateOutputSchema;
121694
122468
  exports.RenewDeviceCertificateResultSchema = RenewDeviceCertificateResultSchema;
@@ -121944,6 +122718,9 @@ exports.SwitchStatusSchema = SwitchStatusSchema;
121944
122718
  exports.SystemHeldSecretNameSchema = SystemHeldSecretNameSchema;
121945
122719
  exports.SystemMetricsSchema = SystemMetricsSchema;
121946
122720
  exports.SystemMirror = SystemMirror;
122721
+ exports.TEMPLATE_KINDS = TEMPLATE_KINDS;
122722
+ exports.TEMPLATE_PAYLOAD_SCHEMAS = TEMPLATE_PAYLOAD_SCHEMAS;
122723
+ exports.TEMPLATE_WRITE_REFUSALS = TEMPLATE_WRITE_REFUSALS;
121947
122724
  exports.TIMELAPSE_DENSE_FLOOR_SEC = TIMELAPSE_DENSE_FLOOR_SEC;
121948
122725
  exports.TIMEZONES = TIMEZONES;
121949
122726
  exports.TRACK_RELATIONS_DEFAULT_LIMIT = TRACK_RELATIONS_DEFAULT_LIMIT;
@@ -121957,6 +122734,11 @@ exports.TargetKindLevelSchema = TargetKindLevelSchema;
121957
122734
  exports.TargetKindSchema = TargetKindSchema;
121958
122735
  exports.TargetSchema = TargetSchema;
121959
122736
  exports.TemperatureSensorStatusSchema = TemperatureSensorStatusSchema;
122737
+ exports.TemplateDeleteResultSchema = TemplateDeleteResultSchema;
122738
+ exports.TemplateKindSchema = TemplateKindSchema;
122739
+ exports.TemplateRecordSchema = TemplateRecordSchema;
122740
+ exports.TemplateUserSchema = TemplateUserSchema;
122741
+ exports.TemplateWriteResultSchema = TemplateWriteResultSchema;
121960
122742
  exports.TerminalInstanceInfoSchema = TerminalInstanceInfoSchema;
121961
122743
  exports.TerminalLegacyCameraSchema = TerminalLegacyCameraSchema;
121962
122744
  exports.TerminalOutputBatchSchema = TerminalOutputBatchSchema;
@@ -122264,6 +123046,7 @@ exports.capacitySessionsOf = capacitySessionsOf;
122264
123046
  exports.carbonMonoxideCapability = carbonMonoxideCapability;
122265
123047
  exports.cellsToRects = cellsToRects;
122266
123048
  exports.checkRoleAddress = checkRoleAddress;
123049
+ exports.checkTemplatePayload = checkTemplatePayload;
122267
123050
  exports.claimedRequestOrigins = claimedRequestOrigins;
122268
123051
  exports.classifyBearerPrincipal = classifyBearerPrincipal;
122269
123052
  exports.classifyCamerasForBenchmark = classifyCamerasForBenchmark;
@@ -122367,7 +123150,9 @@ exports.createSliceHandle = require_sleep.createSliceHandle;
122367
123150
  exports.createStatefulExpressionMemory = require_composition.createStatefulExpressionMemory;
122368
123151
  exports.createSystemProxy = createSystemProxy;
122369
123152
  exports.customAction = require_sleep.customAction;
123153
+ exports.customKeyOfRole = customKeyOfRole;
122370
123154
  exports.customModelRegistryCapability = customModelRegistryCapability;
123155
+ exports.customRole = customRole;
122371
123156
  exports.dataStoreProviderCapability = dataStoreProviderCapability;
122372
123157
  exports.databaseSnapshotCapability = databaseSnapshotCapability;
122373
123158
  exports.dayNightCapability = dayNightCapability;
@@ -122646,6 +123431,9 @@ exports.isOccupancyRule = isOccupancyRule;
122646
123431
  exports.isOwnerOnlyEventCategory = require_event_category.isOwnerOnlyEventCategory;
122647
123432
  exports.isPlugApplianceType = isPlugApplianceType;
122648
123433
  exports.isReadingQuantity = require_sleep.isReadingQuantity;
123434
+ exports.isRemoteCustomKey = isRemoteCustomKey;
123435
+ exports.isRemoteKnownRole = isRemoteKnownRole;
123436
+ exports.isRemoteRole = isRemoteRole;
122649
123437
  exports.isRemovedModelId = isRemovedModelId;
122650
123438
  exports.isRestoredCap = isRestoredCap;
122651
123439
  exports.isRetiredClearKey = isRetiredClearKey;
@@ -122671,6 +123459,7 @@ exports.isSystemEventFamily = isSystemEventFamily;
122671
123459
  exports.isSystemEventRecoveryKind = isSystemEventRecoveryKind;
122672
123460
  exports.isSystemEventSummaryRule = isSystemEventSummaryRule;
122673
123461
  exports.isSystemRuleKind = isSystemRuleKind;
123462
+ exports.isTemplateKind = isTemplateKind;
122674
123463
  exports.isUnfilledRequiredProblem = isUnfilledRequiredProblem;
122675
123464
  exports.isVectorIndexDimConflict = isVectorIndexDimConflict;
122676
123465
  exports.isVoidInput = isVoidInput;
@@ -122930,6 +123719,7 @@ exports.reducePoints = reducePoints;
122930
123719
  exports.reducerForActive = reducerForActive;
122931
123720
  exports.registerKnownSecret = require_sleep.registerKnownSecret;
122932
123721
  exports.remapCropDetections = remapCropDetections;
123722
+ exports.remoteCapability = remoteCapability;
122933
123723
  exports.removeScalarTarget = removeScalarTarget;
122934
123724
  exports.renameAlarmProfile = renameAlarmProfile;
122935
123725
  exports.renderVocabPhrase = renderVocabPhrase;
@@ -123080,6 +123870,7 @@ exports.sleep = require_sleep.sleep;
123080
123870
  exports.sleepCancellable = require_sleep.sleepCancellable;
123081
123871
  exports.sliceActiveValue = require_composition.sliceActiveValue;
123082
123872
  exports.sliceChangedAt = require_composition.sliceChangedAt;
123873
+ exports.smartIrDraft = smartIrDraft;
123083
123874
  exports.smokeCapability = smokeCapability;
123084
123875
  exports.smtpProviderCapability = smtpProviderCapability;
123085
123876
  exports.snapshotCapability = snapshotCapability;
@@ -123151,6 +123942,7 @@ exports.taskLogEntrySchema = taskLogEntrySchema;
123151
123942
  exports.taskPhaseSchema = taskPhaseSchema;
123152
123943
  exports.taskTargetSchema = taskTargetSchema;
123153
123944
  exports.temperatureSensorCapability = temperatureSensorCapability;
123945
+ exports.templateCapability = templateCapability;
123154
123946
  exports.templateCarriesSummary = templateCarriesSummary;
123155
123947
  exports.templateDeviceType = templateDeviceType;
123156
123948
  exports.templateVarDeliveryApplies = templateVarDeliveryApplies;