@camstack/addon-post-analysis 1.2.666 → 1.2.668

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 (16) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/{clip-model-registry-Uc_C1x81.mjs → clip-model-registry-BYJ5lbPi.mjs} +922 -509
  3. package/dist/{clip-model-registry-BIhYJRFt.js → clip-model-registry-DTC5bkf8.js} +922 -509
  4. package/dist/embedding-encoder/index.js +1 -1
  5. package/dist/embedding-encoder/index.mjs +1 -1
  6. package/dist/pipeline-analytics/_stub.js +2 -2
  7. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-FkeZoNXj.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DBEA3TPX.mjs} +2 -2
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DxtNkzmw.mjs +26 -0
  9. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CU7mDiJp.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit-BBhgVoNl.mjs → hostInit-ChYqK-gn.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +460 -116
  12. package/dist/pipeline-analytics/index.mjs +460 -116
  13. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  14. package/package.json +2 -2
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CnYZAdvw.mjs +0 -26
  16. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Br6eWzp_.mjs +0 -26
@@ -816,7 +816,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
816
816
  return EventCategory;
817
817
  }({});
818
818
  //#endregion
819
- //#region ../types/dist/device-type-CfHw_quR.mjs
819
+ //#region ../types/dist/device-type-D1fH5kaC.mjs
820
820
  /**
821
821
  import { errMsg } from '@camstack/types'
822
822
  * Extract a human-readable message from an unknown error value.
@@ -1047,6 +1047,12 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
1047
1047
  DeviceFeature["MediaPlayerPrevious"] = "media-player-previous";
1048
1048
  /** Media player exposes stop distinct from pause. */
1049
1049
  DeviceFeature["MediaPlayerStop"] = "media-player-stop";
1050
+ /** Media player exposes volume up / down in steps (an infrared TV has no level). */
1051
+ DeviceFeature["MediaPlayerVolumeStep"] = "media-player-volume-step";
1052
+ /** Media player exposes channel up / down in steps. */
1053
+ DeviceFeature["MediaPlayerChannelStep"] = "media-player-channel-step";
1054
+ /** Media player exposes power on / off. */
1055
+ DeviceFeature["MediaPlayerPower"] = "media-player-power";
1050
1056
  /** Alarm panel requires a PIN code on arm/disarm. */
1051
1057
  DeviceFeature["AlarmPinRequired"] = "alarm-pin-required";
1052
1058
  /** Presence device carries GPS coordinates (lat/lng/accuracy) in
@@ -6063,7 +6069,7 @@ var ZodIssueCode = {
6063
6069
  var ZodFirstPartyTypeKind;
6064
6070
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6065
6071
  //#endregion
6066
- //#region ../types/dist/sleep-C6LqREpb.mjs
6072
+ //#region ../types/dist/sleep-D1jjWAr8.mjs
6067
6073
  /**
6068
6074
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6069
6075
  * window to float samples (D455).
@@ -6656,7 +6662,7 @@ var NOT_RUN = {
6656
6662
  * throws: an unreachable or refusing engine is logged and reported `declared: false`,
6657
6663
  * because the hub (and this addon's boot) must not hang on a migration.
6658
6664
  */
6659
- async function runAddonMigrations(host, migrations, logger) {
6665
+ async function runAddonMigrations(host, migrations, logger, options = {}) {
6660
6666
  const first = migrations[0];
6661
6667
  if (first === void 0) return NOT_RUN;
6662
6668
  const owner = first.meta.owner;
@@ -6685,7 +6691,9 @@ async function runAddonMigrations(host, migrations, logger) {
6685
6691
  else if (status === "blocked") blocked.push(id);
6686
6692
  else skipped.push(id);
6687
6693
  };
6694
+ const selected = options.only === void 0 ? null : new Set(options.only);
6688
6695
  for (const step of declaration.steps) {
6696
+ if (selected !== null && !selected.has(step.id)) continue;
6689
6697
  const bound = byId.get(step.id);
6690
6698
  if (bound === void 0 || step.action === "skip") {
6691
6699
  skipped.push(step.id);
@@ -7803,14 +7811,18 @@ var BaseAddon = class {
7803
7811
  * nothing; the boot continues. A migration that FAILED (or was blocked by one)
7804
7812
  * and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
7805
7813
  * serve data whose shape it cannot trust; `tolerate` migrations never do.
7814
+ *
7815
+ * ALWAYS pass the whole catalogue; a phase that must run only some of it passes
7816
+ * `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
7817
+ * omitted applied ids "newer").
7806
7818
  */
7807
- async runMigrations(migrations) {
7819
+ async runMigrations(migrations, options) {
7808
7820
  const api = this.ctx.api.migrations;
7809
7821
  const report = await runAddonMigrations({
7810
7822
  declareOwner: (input) => api.declareOwner.mutate(input),
7811
7823
  startMigration: (input) => api.startMigration.mutate(input),
7812
7824
  finishMigration: (input) => api.finishMigration.mutate(input)
7813
- }, migrations, this.ctx.logger);
7825
+ }, migrations, this.ctx.logger, options);
7814
7826
  if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
7815
7827
  return report;
7816
7828
  }
@@ -8151,6 +8163,10 @@ var BaseAddon = class {
8151
8163
  const stored = keychain ? await this.readAddonStoreWithRetry() : {};
8152
8164
  const patch = {};
8153
8165
  for (const key of keys) {
8166
+ if (tree.get(key)?.kind === "array") {
8167
+ patch[key] = [];
8168
+ continue;
8169
+ }
8154
8170
  const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
8155
8171
  patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
8156
8172
  }
@@ -8837,6 +8853,10 @@ var RECORD_SETTINGS_MAX_ROWS = 5e4;
8837
8853
  * (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
8838
8854
  * (`auth.accessToken`, `headers[].value`).
8839
8855
  *
8856
+ * `{}` after a key means "each value of this map" (`applianceSecrets{}`): the map's own key
8857
+ * names the value (`applianceSecrets{fridge}`), so no id key is declared and a body that drops a
8858
+ * key releases exactly that key's secret.
8859
+ *
8840
8860
  * Array elements are matched by a DECLARED id key (`secretArrayKeys`:
8841
8861
  * `{ headers: 'name' }`), never by index, so reordering a list keeps every
8842
8862
  * keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
@@ -8849,11 +8869,13 @@ function parseSecretPath(path) {
8849
8869
  if (path.length === 0) throw new InvalidRecordError("an empty secret path");
8850
8870
  return path.split(".").map((raw) => {
8851
8871
  const each = raw.endsWith("[]");
8852
- const key = each ? raw.slice(0, -2) : raw;
8872
+ const map = raw.endsWith("{}");
8873
+ const key = each || map ? raw.slice(0, -2) : raw;
8853
8874
  if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
8854
8875
  return {
8855
8876
  key,
8856
- each
8877
+ each,
8878
+ map
8857
8879
  };
8858
8880
  });
8859
8881
  }
@@ -8940,7 +8962,8 @@ function isZodSecret(schema) {
8940
8962
  }
8941
8963
  /**
8942
8964
  * Every declared secret path in a config schema: dotted keys, `[]` for "each
8943
- * element of this array" (`connection.password`, `rows[].token`). A declared
8965
+ * element of this array" (`connection.password`, `rows[].token`), `{}` for "each value of this
8966
+ * map" (`applianceSecrets{}`). A declared
8944
8967
  * object or array is reported as one path and not descended into — everything
8945
8968
  * under it is secret.
8946
8969
  */
@@ -8951,6 +8974,7 @@ function collectZodSecretPaths(schema, prefix = "") {
8951
8974
  const def = defOf(layer);
8952
8975
  if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
8953
8976
  else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
8977
+ else if (def.type === "record" && def.valueType) paths.push(...collectZodSecretPaths(def.valueType, `${prefix}{}`));
8954
8978
  else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
8955
8979
  else if (def.type === "intersection") {
8956
8980
  if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
@@ -9090,7 +9114,7 @@ function defineRecordKinds(specs) {
9090
9114
  retention: spec.retention,
9091
9115
  deviceRemoval: spec.deviceRemoval,
9092
9116
  maxDataBytes: spec.maxDataBytes,
9093
- secretPaths: [...secretPaths],
9117
+ secretPaths: [...new Set([...secretPaths, ...spec.retiredSecretPaths ?? []])],
9094
9118
  secretArrayKeys: spec.secretArrayKeys === void 0 ? void 0 : { ...spec.secretArrayKeys },
9095
9119
  transactFamily: spec.transactFamily,
9096
9120
  entry: spec.entry === void 0 ? void 0 : {
@@ -11999,7 +12023,7 @@ function sleep(ms) {
11999
12023
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
12000
12024
  }
12001
12025
  //#endregion
12002
- //#region ../types/dist/composition-Dh3RfMx5.mjs
12026
+ //#region ../types/dist/composition-pOHbTzRB.mjs
12003
12027
  /**
12004
12028
  * Deterministic SHA-256 hash of an arbitrary serialisable value. The
12005
12029
  * canonical form sorts object keys alphabetically at every depth so two
@@ -12922,38 +12946,6 @@ function validateExpressionSource(src) {
12922
12946
  return null;
12923
12947
  }
12924
12948
  /**
12925
- * One power reading becomes a washer, dryer or dishwasher (D944 helper).
12926
- *
12927
- * Standby of the control board is under 5 W; a motor or a heater is far
12928
- * above that, so 5 W is the line. A door lock or a short pump spike is not
12929
- * a cycle, so running starts only after 30 s above the line.
12930
- *
12931
- * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
12932
- * bursts) lasts several minutes. Ending the cycle on the first dip would
12933
- * report "finished" in the middle of the program, so the stop dwell is
12934
- * 10 minutes on every type. The number is stored per type so one machine
12935
- * can move later without moving the others.
12936
- */
12937
- var APPLIANCE_TEMPLATE_ID = "appliance";
12938
- var APPLIANCE_KINDS = [
12939
- "washer",
12940
- "dryer",
12941
- "dishwasher"
12942
- ];
12943
- DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
12944
- /**
12945
- * A dumb plug-in stove or heater, built from a smart plug (a `Heater`).
12946
- *
12947
- * The plug is the actuator: `switch.setState` is a FORWARD to the plug's own
12948
- * (D967), so the heater switches exactly as the plug does. Running is the
12949
- * plug being on; when a power reading is also named, running is the draw
12950
- * staying above the line instead (a plug left on with the stove's own
12951
- * thermostat open is not heating). 50 W is far above any standby and below
12952
- * the smallest radiator.
12953
- */
12954
- var HEATER_TEMPLATE_ID = "heater";
12955
- DeviceType.Heater;
12956
- /**
12957
12949
  * Statistics and integrals over a history read (D944).
12958
12950
  *
12959
12951
  * Pure: the same points always give the same answer, so a restart recomputes
@@ -13097,34 +13089,6 @@ var HelperSpecSchema = discriminatedUnion("kind", [
13097
13089
  /** Actuator groups only. Readings leave this unset. */
13098
13090
  active: ActiveCriterionSchema.optional()
13099
13091
  }),
13100
- object({
13101
- kind: literal("appliance"),
13102
- templateId: literal(APPLIANCE_TEMPLATE_ID),
13103
- appliance: _enum(APPLIANCE_KINDS),
13104
- thresholdWatts: number().positive(),
13105
- startDwellMs: number().int().nonnegative(),
13106
- stopDwellMs: number().int().nonnegative(),
13107
- member: HelperMemberSchema,
13108
- /**
13109
- * The plug that powers the appliance (`switch.on`). Optional: when set, the
13110
- * composition carries a `power-source` feature, so the hub can cut and restore
13111
- * that plug (power-source link of a NEW-target composition).
13112
- */
13113
- powerSwitch: HelperMemberSchema.optional(),
13114
- /** How long a forced restart keeps the plug off; the cap's default when unset. */
13115
- offSeconds: number().optional()
13116
- }),
13117
- object({
13118
- kind: literal("heater"),
13119
- templateId: literal(HEATER_TEMPLATE_ID),
13120
- /** The plug, read and commanded at `switch.on`. */
13121
- plug: HelperMemberSchema,
13122
- /** The plug's power reading, when it has one: running is then the draw, not the switch. */
13123
- meter: HelperMemberSchema.optional(),
13124
- thresholdWatts: number().positive(),
13125
- startDwellMs: number().int().nonnegative(),
13126
- stopDwellMs: number().int().nonnegative()
13127
- }),
13128
13092
  object({
13129
13093
  kind: literal("statistic"),
13130
13094
  stat: _enum(STATISTIC_STATS),
@@ -13296,7 +13260,14 @@ var CompositionForwardCommandSchema = object({
13296
13260
  method: string().min(1),
13297
13261
  args: record(string().min(1), CompositionForwardArgSchema).optional()
13298
13262
  });
13299
- var CompositionCommandTargetSchema = discriminatedUnion("kind", [CompositionForwardCommandSchema, CompositionCodeSourceSchema]);
13263
+ var CompositionCommandTargetSchema = discriminatedUnion("kind", [
13264
+ CompositionForwardCommandSchema,
13265
+ CompositionCodeSourceSchema,
13266
+ object({
13267
+ kind: literal("extension"),
13268
+ target: literal("remote")
13269
+ })
13270
+ ]);
13300
13271
  /** An item key inside an item-array cap (`consumables.items[<key>]`). */
13301
13272
  var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
13302
13273
  /**
@@ -13341,7 +13312,7 @@ var CompositionSchema = object({
13341
13312
  kind: literal("existing"),
13342
13313
  device: CompositionSourceRefSchema
13343
13314
  })]),
13344
- features: array(CompositionFeatureSchema).min(1).max(16),
13315
+ features: array(CompositionFeatureSchema).min(0).max(16),
13345
13316
  /**
13346
13317
  * Present when the block is a helper template. Absent on a free
13347
13318
  * composition (the room example, device 4603). The editor reads this,
@@ -14721,7 +14692,13 @@ var MigrationLedgerEntrySchema = object({
14721
14692
  * joined by the engine from the catalogues it knows this boot. Absent when no
14722
14693
  * catalogue lists the id (a newer database).
14723
14694
  */
14724
- meta: MigrationMetaSchema.optional()
14695
+ meta: MigrationMetaSchema.optional(),
14696
+ /**
14697
+ * Set when this is an applied row of an addon owner whose catalogue the answering
14698
+ * process has not loaded (the owner has not declared yet): the row is applied, the
14699
+ * process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
14700
+ */
14701
+ ownerNotLoaded: boolean().optional()
14725
14702
  });
14726
14703
  /** A catalogued migration that is not applied, with where it stands. */
14727
14704
  var MigrationPendingEntrySchema = object({
@@ -20563,6 +20540,48 @@ var APPLIANCE_CAP_VOCABULARY = {
20563
20540
  scope: "device",
20564
20541
  noun: t("Media player", "Lettore multimediale"),
20565
20542
  actions: [
20543
+ {
20544
+ id: "media.turnOn",
20545
+ method: "turnOn",
20546
+ label: t("Turn on", "Accendi"),
20547
+ phrase: t("Turn on {device}", "Accendi {device}"),
20548
+ icon: "power"
20549
+ },
20550
+ {
20551
+ id: "media.turnOff",
20552
+ method: "turnOff",
20553
+ label: t("Turn off", "Spegni"),
20554
+ phrase: t("Turn off {device}", "Spegni {device}"),
20555
+ icon: "power"
20556
+ },
20557
+ {
20558
+ id: "media.volumeUp",
20559
+ method: "volumeUp",
20560
+ label: t("Volume up", "Alza il volume"),
20561
+ phrase: t("Raise the volume of {device}", "Alza il volume di {device}"),
20562
+ icon: "volume-2"
20563
+ },
20564
+ {
20565
+ id: "media.volumeDown",
20566
+ method: "volumeDown",
20567
+ label: t("Volume down", "Abbassa il volume"),
20568
+ phrase: t("Lower the volume of {device}", "Abbassa il volume di {device}"),
20569
+ icon: "volume-2"
20570
+ },
20571
+ {
20572
+ id: "media.channelUp",
20573
+ method: "channelUp",
20574
+ label: t("Channel up", "Canale successivo"),
20575
+ phrase: t("Go to the next channel on {device}", "Passa al canale successivo su {device}"),
20576
+ icon: "skip-forward"
20577
+ },
20578
+ {
20579
+ id: "media.channelDown",
20580
+ method: "channelDown",
20581
+ label: t("Channel down", "Canale precedente"),
20582
+ phrase: t("Go to the previous channel on {device}", "Torna al canale precedente su {device}"),
20583
+ icon: "skip-back"
20584
+ },
20566
20585
  {
20567
20586
  id: "media.play",
20568
20587
  method: "play",
@@ -50365,6 +50384,244 @@ var controlCapability = {
50365
50384
  */
50366
50385
  history: "none"
50367
50386
  };
50387
+ /**
50388
+ * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
50389
+ * Apple TV, Spotify, Roku, generic OTT receivers, …) on
50390
+ * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
50391
+ * surface — those use `DeviceType.Camera` + the broker pipeline.
50392
+ *
50393
+ * The cap declares a wide method surface; not every backend supports
50394
+ * every method. Drivers signal sub-capability via the
50395
+ * `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
50396
+ * corresponding method) so the UI gates buttons without a slice
50397
+ * fetch — same pattern as `PanTiltZoom` for cameras.
50398
+ */
50399
+ var MediaPlayerStateSchema = _enum([
50400
+ "off",
50401
+ "on",
50402
+ "idle",
50403
+ "playing",
50404
+ "paused",
50405
+ "buffering",
50406
+ "standby"
50407
+ ]);
50408
+ var MediaPlayerRepeatSchema = _enum([
50409
+ "off",
50410
+ "all",
50411
+ "one"
50412
+ ]);
50413
+ var MediaInfoSchema = object({
50414
+ /** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
50415
+ * `podcast`, …). */
50416
+ type: string(),
50417
+ /** Human-readable title. */
50418
+ title: string(),
50419
+ /** Optional artist / channel / station label. */
50420
+ artist: string().optional(),
50421
+ /** Optional album / season / show name. */
50422
+ album: string().optional(),
50423
+ /** Optional cover-art / thumbnail URL. */
50424
+ imageUrl: string().optional()
50425
+ });
50426
+ /**
50427
+ * Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
50428
+ * the device said so. `assumed`: the driver has no feedback (infrared) and
50429
+ * tracks what it last SENT; it moves only after a send was accepted, and a
50430
+ * human at the real remote makes it wrong. A UI shows an assumed value as a
50431
+ * guess, never as a reading.
50432
+ */
50433
+ var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
50434
+ /** The fields of a media-player status a driver can only guess. */
50435
+ var MediaPlayerAssumedFieldSchema = _enum([
50436
+ "state",
50437
+ "volumeLevel",
50438
+ "isMuted",
50439
+ "source"
50440
+ ]);
50441
+ var MediaPlayerStatusSchema = object({
50442
+ /** Playback lifecycle state. */
50443
+ state: MediaPlayerStateSchema,
50444
+ /**
50445
+ * Whether the state fields are readings or the driver's own bookkeeping.
50446
+ * A slice written before this field existed parses as `reported`.
50447
+ */
50448
+ stateSource: MediaPlayerStateSourceSchema.default("reported"),
50449
+ /**
50450
+ * Which fields are the driver's own guess. `stateSource` is `assumed` when
50451
+ * this is not empty; a field not listed here is a reading even then (power
50452
+ * from a running-state source, mute guessed from the last send).
50453
+ */
50454
+ assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
50455
+ /** Volume as 0..100 inclusive. Null when the device has no volume
50456
+ * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
50457
+ volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
50458
+ /** Mute toggle distinct from volume=0. Null when no mute surface.
50459
+ * Pair with `DeviceFeature.MediaPlayerMute`. */
50460
+ isMuted: boolean().nullable(),
50461
+ /** Active source / input. Empty when no source surface. */
50462
+ source: string(),
50463
+ /** Selectable sources. Empty when no source surface.
50464
+ * Pair with `DeviceFeature.MediaPlayerSelectSource`. */
50465
+ availableSources: array(string()),
50466
+ /** Currently-playing media info. Null when nothing is playing. */
50467
+ currentMedia: MediaInfoSchema.nullable(),
50468
+ /** Current playback position in ms. Null when not seekable or
50469
+ * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
50470
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
50471
+ /** Total duration of the current media in ms. Null when unknown
50472
+ * (live stream) or nothing is playing. */
50473
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
50474
+ /** Shuffle toggle. Null when no shuffle surface.
50475
+ * Pair with `DeviceFeature.MediaPlayerShuffle`. */
50476
+ shuffle: boolean().nullable(),
50477
+ /** Repeat mode. Null when no repeat surface.
50478
+ * Pair with `DeviceFeature.MediaPlayerRepeat`. */
50479
+ repeat: MediaPlayerRepeatSchema.nullable(),
50480
+ /** Ms epoch when the slice was last updated. */
50481
+ lastChangedAt: number()
50482
+ });
50483
+ var stepInput = object({
50484
+ deviceId: number().int().nonnegative(),
50485
+ /** How many presses: the caller's count (Alexa `volumeSteps`, `channelCount`). */
50486
+ steps: number().int().min(1).max(50).default(1)
50487
+ });
50488
+ var deviceOnly = object({ deviceId: number().int().nonnegative() });
50489
+ var mediaPlayerCapability = {
50490
+ name: "media-player",
50491
+ scope: "device",
50492
+ deviceNative: true,
50493
+ mode: "singleton",
50494
+ deviceTypes: [DeviceType.MediaPlayer],
50495
+ methods: {
50496
+ turnOn: method(deviceOnly, _void(), {
50497
+ kind: "mutation",
50498
+ auth: "protected"
50499
+ }),
50500
+ turnOff: method(deviceOnly, _void(), {
50501
+ kind: "mutation",
50502
+ auth: "protected"
50503
+ }),
50504
+ volumeUp: method(stepInput, _void(), {
50505
+ kind: "mutation",
50506
+ auth: "protected"
50507
+ }),
50508
+ volumeDown: method(stepInput, _void(), {
50509
+ kind: "mutation",
50510
+ auth: "protected"
50511
+ }),
50512
+ channelUp: method(stepInput, _void(), {
50513
+ kind: "mutation",
50514
+ auth: "protected"
50515
+ }),
50516
+ channelDown: method(stepInput, _void(), {
50517
+ kind: "mutation",
50518
+ auth: "protected"
50519
+ }),
50520
+ play: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50521
+ kind: "mutation",
50522
+ auth: "protected"
50523
+ }),
50524
+ pause: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50525
+ kind: "mutation",
50526
+ auth: "protected"
50527
+ }),
50528
+ stop: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50529
+ kind: "mutation",
50530
+ auth: "protected"
50531
+ }),
50532
+ next: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50533
+ kind: "mutation",
50534
+ auth: "protected"
50535
+ }),
50536
+ previous: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50537
+ kind: "mutation",
50538
+ auth: "protected"
50539
+ }),
50540
+ seek: method(object({
50541
+ deviceId: number().int().nonnegative(),
50542
+ position: number().int().nonnegative()
50543
+ }), _void(), {
50544
+ kind: "mutation",
50545
+ auth: "protected"
50546
+ }),
50547
+ setVolume: method(object({
50548
+ deviceId: number().int().nonnegative(),
50549
+ volumeLevel: number().min(0).max(100)
50550
+ }), _void(), {
50551
+ kind: "mutation",
50552
+ auth: "protected"
50553
+ }),
50554
+ setMute: method(object({
50555
+ deviceId: number().int().nonnegative(),
50556
+ muted: boolean()
50557
+ }), _void(), {
50558
+ kind: "mutation",
50559
+ auth: "protected"
50560
+ }),
50561
+ setShuffle: method(object({
50562
+ deviceId: number().int().nonnegative(),
50563
+ shuffle: boolean()
50564
+ }), _void(), {
50565
+ kind: "mutation",
50566
+ auth: "protected"
50567
+ }),
50568
+ setRepeat: method(object({
50569
+ deviceId: number().int().nonnegative(),
50570
+ repeat: MediaPlayerRepeatSchema
50571
+ }), _void(), {
50572
+ kind: "mutation",
50573
+ auth: "protected"
50574
+ }),
50575
+ selectSource: method(object({
50576
+ deviceId: number().int().nonnegative(),
50577
+ source: string().min(1)
50578
+ }), _void(), {
50579
+ kind: "mutation",
50580
+ auth: "protected"
50581
+ }),
50582
+ playMedia: method(object({
50583
+ deviceId: number().int().nonnegative(),
50584
+ /** Media identifier / URL. */
50585
+ mediaId: string().min(1),
50586
+ /** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
50587
+ * passes it through to the upstream service. */
50588
+ mediaType: string().min(1)
50589
+ }), _void(), {
50590
+ kind: "mutation",
50591
+ auth: "protected"
50592
+ })
50593
+ },
50594
+ status: {
50595
+ schema: MediaPlayerStatusSchema,
50596
+ kind: "push"
50597
+ },
50598
+ /**
50599
+ * Runtime-state slice — mirrored by the kernel. UI players read the
50600
+ * full slice for live now-playing, volume, and progress updates
50601
+ * without polling.
50602
+ */
50603
+ runtimeState: MediaPlayerStatusSchema,
50604
+ /**
50605
+ * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
50606
+ *
50607
+ * See `RuntimeStateDurability`. Enforced by
50608
+ * `scripts/check-runtime-state-durability.ts`.
50609
+ */
50610
+ durability: "session",
50611
+ /**
50612
+ * History (D850): volume (a level an operator or an automation sets; the
50613
+ * default 10 s floor folds a slider drag into its end value) and mute.
50614
+ * `position` is not recorded: it moves continuously while playing.
50615
+ */
50616
+ history: { fields: {
50617
+ volumeLevel: {
50618
+ kind: "gauge",
50619
+ unit: "%",
50620
+ deadband: 1
50621
+ },
50622
+ isMuted: { kind: "boolean" }
50623
+ } }
50624
+ };
50368
50625
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater;
50369
50626
  var NUMBER = {
50370
50627
  kind: "fixed",
@@ -50812,117 +51069,6 @@ function actuator(input) {
50812
51069
  };
50813
51070
  }
50814
51071
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
50815
- var PowerSourceRefusalReasonSchema = _enum([
50816
- "no-power-source",
50817
- "switch-missing",
50818
- "switch-offline",
50819
- "switch-unknown",
50820
- "restart-in-progress",
50821
- "switch-call-failed"
50822
- ]);
50823
- var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
50824
- accepted: literal(false),
50825
- reason: PowerSourceRefusalReasonSchema,
50826
- /** One line for the operator, naming the switch. */
50827
- message: string()
50828
- })]);
50829
- var PowerSourceInfoSchema = object({
50830
- /** False when the device has no power source linked; the rest is then empty. */
50831
- linked: boolean(),
50832
- switchDeviceId: number().nullable(),
50833
- switchName: string().nullable(),
50834
- /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
50835
- switchOn: boolean().nullable(),
50836
- switchOnline: boolean().nullable(),
50837
- restartInProgress: boolean(),
50838
- offSeconds: number().int()
50839
- });
50840
- /** The composed slice: the switch state the link mirrors, plus the stored off time. */
50841
- var PowerSourceStateSchema = object({
50842
- /** Null while the switch has not reported: unknown is never `false` (D393). */
50843
- switchOn: boolean().nullable(),
50844
- offSeconds: number().int(),
50845
- lastFetchedAt: number()
50846
- });
50847
- var POWER_SOURCE_DEVICE_TYPES = [
50848
- DeviceType.Gateway,
50849
- DeviceType.Hub,
50850
- DeviceType.Camera,
50851
- DeviceType.Sensor,
50852
- DeviceType.MediaPlayer,
50853
- DeviceType.Vacuum,
50854
- DeviceType.Generic,
50855
- DeviceType.Washer,
50856
- DeviceType.Dryer,
50857
- DeviceType.Dishwasher,
50858
- DeviceType.PetFountain,
50859
- DeviceType.Fan,
50860
- DeviceType.Humidifier,
50861
- DeviceType.Climate,
50862
- DeviceType.WaterHeater
50863
- ];
50864
- var powerSourceCustomize = {
50865
- version: 1,
50866
- targets: [{
50867
- id: "switch",
50868
- cardinality: "scalar",
50869
- vocabulary: {
50870
- cap: "power-source",
50871
- stateField: "switchOn"
50872
- },
50873
- deviceTypes: POWER_SOURCE_DEVICE_TYPES,
50874
- apply: "add",
50875
- path: "switchOn",
50876
- sources: [{
50877
- kind: "switch",
50878
- transforms: [{ kind: "identity" }]
50879
- }],
50880
- companions: [{
50881
- kind: "constant",
50882
- path: "offSeconds",
50883
- value: 10,
50884
- when: "add"
50885
- }, {
50886
- kind: "clock",
50887
- path: "lastFetchedAt",
50888
- when: "always"
50889
- }],
50890
- sourceReport: { status: "assignment-status" }
50891
- }]
50892
- };
50893
- var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
50894
- var powerSourceCapability = {
50895
- name: "power-source",
50896
- scope: "device",
50897
- mode: "singleton",
50898
- deviceTypes: POWER_SOURCE_DEVICE_TYPES,
50899
- methods: {
50900
- getStatus: systemMethod(deviceInput$2, PowerSourceInfoSchema, { auth: "admin" }),
50901
- powerOn: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
50902
- kind: "mutation",
50903
- auth: "admin"
50904
- }),
50905
- powerOff: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
50906
- kind: "mutation",
50907
- auth: "admin"
50908
- }),
50909
- forceRestart: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
50910
- kind: "mutation",
50911
- auth: "admin"
50912
- })
50913
- },
50914
- runtimeState: PowerSourceStateSchema,
50915
- customize: powerSourceCustomize,
50916
- /**
50917
- * Runtime-state durability: **session** — mirrored from the linked switch on
50918
- * its next report; a restored value would outlive the reading it names.
50919
- *
50920
- * See `RuntimeStateDurability`. Enforced by
50921
- * `scripts/check-runtime-state-durability.ts`.
50922
- */
50923
- durability: "session",
50924
- history: "none"
50925
- };
50926
51072
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
50927
51073
  DeviceType.Camera;
50928
51074
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
@@ -50950,6 +51096,168 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
50950
51096
  }).optional()
50951
51097
  })) });
50952
51098
  /**
51099
+ * A saved Customize assignment (D936). The schema is the type: callers use
51100
+ * `z.infer`, they do not restate the shape.
51101
+ */
51102
+ var OverrideSourceSchema = object({
51103
+ addonId: string().min(1),
51104
+ stableId: string().min(1),
51105
+ cap: string().min(1),
51106
+ fieldPath: string().min(1)
51107
+ });
51108
+ var IdentityTransformDraftSchema = object({ kind: literal("identity") });
51109
+ var InvertTransformDraftSchema = object({ kind: literal("invert") });
51110
+ var ThresholdTransformDraftSchema = object({
51111
+ kind: literal("threshold"),
51112
+ comparison: _enum(["gte", "lte"]),
51113
+ threshold: number(),
51114
+ startDwellMs: number().nonnegative(),
51115
+ stopDwellMs: number().nonnegative()
51116
+ });
51117
+ var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
51118
+ IdentityTransformDraftSchema,
51119
+ InvertTransformDraftSchema,
51120
+ ThresholdTransformDraftSchema,
51121
+ object({
51122
+ kind: literal("linear"),
51123
+ scale: number(),
51124
+ offset: number(),
51125
+ clampMin: number().optional(),
51126
+ clampMax: number().optional()
51127
+ }),
51128
+ object({
51129
+ kind: literal("enum-map"),
51130
+ mapping: record(string(), string().nullable())
51131
+ }),
51132
+ object({ kind: literal("number") })
51133
+ ]);
51134
+ var ScalarOverrideDraftSchema = object({
51135
+ kind: literal("scalar"),
51136
+ cap: string().min(1),
51137
+ targetId: string().min(1),
51138
+ source: OverrideSourceSchema,
51139
+ transform: CustomizeTransformDraftSchema
51140
+ });
51141
+ var KeyedArrayOverrideDraftSchema = object({
51142
+ kind: literal("keyedArray"),
51143
+ cap: string().min(1),
51144
+ targetId: string().min(1),
51145
+ key: string().min(1),
51146
+ label: string().min(1),
51147
+ fieldPath: string().min(1),
51148
+ source: OverrideSourceSchema,
51149
+ transform: CustomizeTransformDraftSchema
51150
+ });
51151
+ var PulseOverrideDraftSchema = object({
51152
+ kind: literal("pulse"),
51153
+ cap: string().min(1),
51154
+ targetId: string().min(1),
51155
+ enabled: boolean(),
51156
+ debounceMs: number().nonnegative(),
51157
+ edges: array(_enum([
51158
+ "rising",
51159
+ "button",
51160
+ "native-event"
51161
+ ])).min(1).optional(),
51162
+ sources: array(OverrideSourceSchema).min(1)
51163
+ });
51164
+ var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
51165
+ "in-addition",
51166
+ "confirm",
51167
+ "replace"
51168
+ ]).optional() });
51169
+ discriminatedUnion("kind", [
51170
+ ScalarOverrideDraftSchema,
51171
+ KeyedArrayOverrideDraftSchema,
51172
+ PulseOverrideDraftSchema,
51173
+ object({
51174
+ kind: literal("span"),
51175
+ cap: string().min(1),
51176
+ targetId: string().min(1),
51177
+ timeoutMs: number().nonnegative(),
51178
+ cooldownMs: number().nonnegative().optional(),
51179
+ sources: array(SpanSourceDraftSchema)
51180
+ })
51181
+ ]);
51182
+ object({
51183
+ targetId: string().min(1),
51184
+ configured: boolean(),
51185
+ source: _enum(["native", "assigned"]),
51186
+ state: _enum([
51187
+ "active",
51188
+ "stale",
51189
+ "released",
51190
+ "unavailable"
51191
+ ]),
51192
+ reason: string().nullable()
51193
+ });
51194
+ /**
51195
+ * What a running-threshold proposal says (planning#405). The schema is the
51196
+ * type: the algorithm, the capability method and the UI all read `z.infer`.
51197
+ *
51198
+ * A proposal is read-only advice: it carries the numbers it was derived from
51199
+ * (`evidence`) so the operator can judge it, and an `insufficient` answer names
51200
+ * WHY with a typed reason and the catalog defaults, never an invented number.
51201
+ */
51202
+ var RunningProposalTierSchema = _enum(["raw", "5m"]);
51203
+ var RunningProposalConfidenceSchema = _enum([
51204
+ "high",
51205
+ "medium",
51206
+ "low"
51207
+ ]);
51208
+ var RunningProposalInsufficientReasonSchema = _enum([
51209
+ "no-history",
51210
+ "span-too-short",
51211
+ "truncated",
51212
+ "no-idle-floor",
51213
+ "no-runs",
51214
+ "no-separation"
51215
+ ]);
51216
+ var RunningProposalReplaySchema = object({
51217
+ /** Intervals the real dwell expression held true over the window. */
51218
+ intervals: number().int().nonnegative(),
51219
+ /** Intervals outside every run, or a second interval inside one. */
51220
+ extra: number().int().nonnegative(),
51221
+ /** Runs without an interval of their own (none, or merged into a neighbour). */
51222
+ missed: number().int().nonnegative()
51223
+ });
51224
+ var RunningProposalEvidenceSchema = object({
51225
+ tier: RunningProposalTierSchema,
51226
+ spanMs: number().nonnegative(),
51227
+ from: number(),
51228
+ to: number(),
51229
+ points: number().int().nonnegative(),
51230
+ runsFound: number().int().nonnegative(),
51231
+ pausesFound: number().int().nonnegative(),
51232
+ longestPauseMs: number().nonnegative(),
51233
+ /** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
51234
+ pulsesFound: number().int().nonnegative(),
51235
+ longestPulseMs: number().nonnegative(),
51236
+ idleFloorW: number(),
51237
+ noiseCeilingW: number(),
51238
+ plateauW: number(),
51239
+ replay: RunningProposalReplaySchema
51240
+ });
51241
+ var RunningProposalInsufficientEvidenceSchema = object({
51242
+ spanMs: number().nonnegative(),
51243
+ points: number().int().nonnegative(),
51244
+ runsFound: number().int().nonnegative()
51245
+ });
51246
+ var RunningThresholdProposalSchema = discriminatedUnion("state", [object({
51247
+ state: literal("proposal"),
51248
+ transform: ThresholdTransformDraftSchema,
51249
+ evidence: RunningProposalEvidenceSchema,
51250
+ confidence: RunningProposalConfidenceSchema,
51251
+ /** Why the confidence is not higher; empty when nothing held it back. */
51252
+ notes: array(string())
51253
+ }), object({
51254
+ state: literal("insufficient"),
51255
+ reason: RunningProposalInsufficientReasonSchema,
51256
+ evidence: RunningProposalInsufficientEvidenceSchema,
51257
+ /** The catalog defaults, offered as what stands when nothing better is known. */
51258
+ fallback: ThresholdTransformDraftSchema
51259
+ })]);
51260
+ /**
50953
51261
  * The report of a one-shot "move this owner's plaintext secrets into the
50954
51262
  * keychain" run (D878). COUNTS only - never a value, never a ref.
50955
51263
  *
@@ -51246,6 +51554,14 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
51246
51554
  kind: "mutation",
51247
51555
  auth: "admin",
51248
51556
  access: "view"
51557
+ }), method(object({
51558
+ deviceId: number().int(),
51559
+ cap: string().min(1),
51560
+ fieldPath: string().min(1),
51561
+ deviceType: _enum(DeviceType).optional()
51562
+ }), RunningThresholdProposalSchema, {
51563
+ auth: "admin",
51564
+ access: "view"
51249
51565
  }), method(object({
51250
51566
  blockId: string().min(1),
51251
51567
  trace: AutomationRunTraceSchema.optional(),
@@ -52207,7 +52523,27 @@ method(object({
52207
52523
  * exposes only the COMMON surface: link state, supported device
52208
52524
  * kinds (so the UI can filter the device picker), and per-device
52209
52525
  * expose/unexpose.
52210
- */
52526
+ *
52527
+ * INSTANCES. One export addon can serve several independent targets (a
52528
+ * HomeKit bridge, a Home Assistant broker). `listInstances` names them.
52529
+ * `instanceId` on a READ is optional: absent = the whole addon (linked if any
52530
+ * enabled instance is, `exposedDeviceCount` = distinct devices, `setup` = the
52531
+ * default instance's); present = that instance; an id the provider does not
52532
+ * serve is refused with `ExportInstanceUnknownError` (`export-instance-unknown`),
52533
+ * never answered empty. `instanceId` on a WRITE is REQUIRED: a membership
52534
+ * change always names the target it changes. A single-instance provider
52535
+ * accepts an absent id on a read or its own id, and refuses any other.
52536
+ */
52537
+ /** Which instance of an export addon a call addresses. Absent on a read = the whole addon. */
52538
+ var InstanceIdSchema$1 = string().min(1);
52539
+ var InstancePinSchema = object({ instanceId: InstanceIdSchema$1.optional() });
52540
+ /** One independent export target of an addon. */
52541
+ var ExportInstanceSchema = object({
52542
+ instanceId: InstanceIdSchema$1,
52543
+ name: string(),
52544
+ enabled: boolean(),
52545
+ kind: string().optional()
52546
+ });
52211
52547
  var LinkStateSchema = _enum([
52212
52548
  "unlinked",
52213
52549
  "linked",
@@ -52258,10 +52594,14 @@ var ExposedDeviceSchema = object({
52258
52594
  capabilities: array(string()).optional()
52259
52595
  });
52260
52596
  var ExposeInputSchema = object({
52597
+ instanceId: InstanceIdSchema$1,
52261
52598
  deviceId: string(),
52262
52599
  capabilities: array(string()).optional()
52263
52600
  });
52264
- var UnexposeInputSchema = object({ deviceId: string() });
52601
+ var UnexposeInputSchema = object({
52602
+ instanceId: InstanceIdSchema$1,
52603
+ deviceId: string()
52604
+ });
52265
52605
  /**
52266
52606
  * What an exporter may expose besides devices (D968). An action and a
52267
52607
  * notification target are LIST ENTRIES, never devices (D954): they enter an
@@ -52290,15 +52630,21 @@ var ExposedEntitySchema = object({
52290
52630
  entity: ExportEntityRefSchema,
52291
52631
  exposedAs: string().optional()
52292
52632
  });
52293
- var ExposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
52294
- var UnexposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
52295
- method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchema)), method(_void(), array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
52633
+ var ExposeEntityInputSchema = object({
52634
+ instanceId: InstanceIdSchema$1,
52635
+ entity: ExportEntityRefSchema
52636
+ });
52637
+ var UnexposeEntityInputSchema = object({
52638
+ instanceId: InstanceIdSchema$1,
52639
+ entity: ExportEntityRefSchema
52640
+ });
52641
+ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(ExportInstanceSchema)), method(InstancePinSchema, array(DeviceKindSchema)), method(InstancePinSchema, array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
52296
52642
  kind: "mutation",
52297
52643
  auth: "admin"
52298
52644
  }), method(UnexposeInputSchema, _void(), {
52299
52645
  kind: "mutation",
52300
52646
  auth: "admin"
52301
- }), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(_void(), array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
52647
+ }), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(InstancePinSchema, array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
52302
52648
  kind: "mutation",
52303
52649
  auth: "admin"
52304
52650
  }), optionalMethod(UnexposeEntityInputSchema, _void(), {
@@ -55943,15 +56289,15 @@ var FanPresetCaptureResultSchema = object({
55943
56289
  items: array(FanCaptureItemSchema),
55944
56290
  omitted: array(FanCaptureOmissionSchema)
55945
56291
  });
55946
- var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
56292
+ var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
55947
56293
  DeviceType.Fan;
55948
- method(deviceInput$1, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$1.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
56294
+ method(deviceInput$2, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$2.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
55949
56295
  kind: "mutation",
55950
56296
  auth: "admin"
55951
- }), method(deviceInput$1.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
56297
+ }), method(deviceInput$2.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
55952
56298
  kind: "mutation",
55953
56299
  auth: "protected"
55954
- }), method(deviceInput$1.extend({
56300
+ }), method(deviceInput$2.extend({
55955
56301
  fields: array(FanCaptureFieldSchema).optional(),
55956
56302
  steps: array(ActionStepSchema).max(20).default([])
55957
56303
  }), FanPresetCaptureResultSchema, { auth: "protected" });
@@ -56834,6 +57180,12 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
56834
57180
  }), object({ success: boolean() }), {
56835
57181
  kind: "mutation",
56836
57182
  auth: "admin"
57183
+ }), method(object({ id: string() }), object({
57184
+ rows: number().int().nonnegative(),
57185
+ secrets: number().int().nonnegative()
57186
+ }), {
57187
+ kind: "mutation",
57188
+ auth: "admin"
56837
57189
  }), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
56838
57190
  addonId: string(),
56839
57191
  settings: record(string(), unknown())
@@ -56862,13 +57214,65 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
56862
57214
  */
56863
57215
  var ConfigSchemaPassthrough$3 = custom();
56864
57216
  var IntegrationInstanceIdSchema = string().min(1);
57217
+ var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
57218
+ /**
57219
+ * One kind of instance a provider can create (an adapter, an account type). The sign-in and
57220
+ * connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
57221
+ * hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
57222
+ * interactive account link (popup) and may carry `{{brokerId}}`, which the UI replaces with the
57223
+ * instance id.
57224
+ */
57225
+ var IntegrationInstanceKindSchema = object({
57226
+ /** Stable id of the kind, sent back as `createInstance.kind`. */
57227
+ id: string().min(1),
57228
+ label: string().min(1),
57229
+ /** The kind signs in through a server-held interactive session. */
57230
+ signIn: object({
57231
+ basePath: string().min(1),
57232
+ methods: array(_enum(["device-code", "proxy"])).optional()
57233
+ }).optional(),
57234
+ /** Relative hub path to start an interactive account link of an instance of this kind. */
57235
+ connectPath: string().min(1).optional(),
57236
+ /** Relative hub path to disconnect that account. */
57237
+ disconnectPath: string().min(1).optional()
57238
+ });
56865
57239
  var IntegrationInstanceDescriptionSchema = object({
57240
+ /** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
57241
+ role: _enum([
57242
+ "devices",
57243
+ "services",
57244
+ "both"
57245
+ ]),
57246
+ /** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
57247
+ discovery: object({
57248
+ mode: _enum([
57249
+ "none",
57250
+ "lan",
57251
+ "account"
57252
+ ]),
57253
+ scopedToInstance: boolean()
57254
+ }).optional(),
56866
57255
  /** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
56867
- instanceMode: _enum(["single", "multiple"]),
57256
+ instanceMode: IntegrationInstanceModeSchema,
56868
57257
  /** What the instance is, for the UI (`destination`, `relay`, `account`). */
56869
57258
  kind: string(),
56870
57259
  /** True when `testInstance` really probes the remote. */
56871
- canTest: boolean()
57260
+ canTest: boolean(),
57261
+ /**
57262
+ * The kinds an instance can be of. Absent or one entry: nothing to choose; more than one: the
57263
+ * Add flow asks which first and sends it as `createInstance.kind`.
57264
+ */
57265
+ kinds: array(IntegrationInstanceKindSchema).optional(),
57266
+ /**
57267
+ * The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
57268
+ * devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
57269
+ */
57270
+ allowRestoreId: boolean().optional(),
57271
+ /**
57272
+ * `createInstance` without an `id` works: the provider mints it. Absent means the caller must
57273
+ * send an id (the UI then mints an opaque one).
57274
+ */
57275
+ mintsId: boolean().optional()
56872
57276
  });
56873
57277
  var IntegrationInstanceStatusSchema = object({
56874
57278
  state: _enum([
@@ -56877,7 +57281,9 @@ var IntegrationInstanceStatusSchema = object({
56877
57281
  "error",
56878
57282
  "unknown"
56879
57283
  ]),
56880
- detail: string().optional()
57284
+ detail: string().optional(),
57285
+ /** A short human word for the state ("Signed in", "Token expired"); shown instead of `state`. */
57286
+ label: string().optional()
56881
57287
  });
56882
57288
  var IntegrationInstanceSchema = object({
56883
57289
  id: IntegrationInstanceIdSchema,
@@ -56885,13 +57291,18 @@ var IntegrationInstanceSchema = object({
56885
57291
  /** The addon's own rule (an URL is present, a client id is present...). */
56886
57292
  configured: boolean(),
56887
57293
  /**
56888
- * The instance's own on/off switch (D996): always present, derived from the record body
56889
- * (`enabled !== false`, so a row written before the field existed is on).
57294
+ * The instance's own on/off switch (D996), derived from the record body (`enabled !== false`,
57295
+ * so a row written before the field existed is on). Absent when the `integrations` registry
57296
+ * owns the switch (`describe().role` is `devices`): read it from the registry row.
56890
57297
  */
56891
- enabled: boolean(),
57298
+ enabled: boolean().optional(),
57299
+ /** The kind of this instance (one of `describe().kinds[].id`), when the provider has kinds. */
57300
+ kind: string().optional(),
56892
57301
  /** Values for the instance form; a secret is a `SecretFieldView`, never text. */
56893
57302
  formValues: record(string(), unknown()),
56894
- status: IntegrationInstanceStatusSchema.optional()
57303
+ status: IntegrationInstanceStatusSchema.optional(),
57304
+ /** Epoch ms of the last probe of the remote that produced `status`; absent when never checked. */
57305
+ lastCheckedAt: number().optional()
56895
57306
  });
56896
57307
  var IntegrationInstancePatchSchema = object({
56897
57308
  id: IntegrationInstanceIdSchema,
@@ -56900,8 +57311,9 @@ var IntegrationInstancePatchSchema = object({
56900
57311
  values: record(string(), unknown())
56901
57312
  });
56902
57313
  method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3, { auth: "admin" }), method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }), method(object({
56903
- id: IntegrationInstanceIdSchema,
56904
- name: string().min(1)
57314
+ id: IntegrationInstanceIdSchema.optional(),
57315
+ name: string().min(1),
57316
+ kind: string().min(1).optional()
56905
57317
  }), object({ id: string() }), {
56906
57318
  kind: "mutation",
56907
57319
  auth: "admin"
@@ -59061,6 +59473,11 @@ var LogEntrySchema = object({
59061
59473
  * flight, and not asking for the next slice ends the scan.
59062
59474
  */
59063
59475
  var LogReadQuerySchema = object({
59476
+ /**
59477
+ * Which destination INSTANCE answers (a destination addon may hold several records). Absent ⇒
59478
+ * the provider's first readable one, so a caller written before instances existed is unchanged.
59479
+ */
59480
+ instanceId: string().min(1).max(200).optional(),
59064
59481
  /** Correlates `cancelRead`. Caller-chosen, unique per in-flight read. */
59065
59482
  requestId: string().min(1).max(64).optional(),
59066
59483
  /** The device the line is about (`tags.deviceId`, numeric). */
@@ -59123,6 +59540,8 @@ var LogReadResultSchema = object({
59123
59540
  /** True when `cancelRead` ended this slice early. */
59124
59541
  cancelled: boolean().optional()
59125
59542
  });
59543
+ /** Names one destination instance; absent ⇒ the provider's first readable one. */
59544
+ var LogInstanceSelectorSchema = object({ instanceId: string().min(1).max(200).optional() });
59126
59545
  var LogReadCapabilitiesSchema = object({
59127
59546
  /** False ⇒ this destination only ships lines (console, forwarder, an unconfigured Loki). */
59128
59547
  readable: boolean(),
@@ -59141,7 +59560,7 @@ var LogReadCapabilitiesSchema = object({
59141
59560
  /** How far back the store reaches, when the provider knows (days). */
59142
59561
  retentionDays: number().nullable()
59143
59562
  });
59144
- method(LogEntrySchema, _void(), { kind: "mutation" }), optionalMethod(_void(), LogReadCapabilitiesSchema, { auth: "admin" }), optionalMethod(LogReadQuerySchema, LogReadResultSchema, { auth: "admin" }), optionalMethod(object({ requestId: string().min(1).max(64) }), object({ cancelled: boolean() }), {
59563
+ method(LogEntrySchema, _void(), { kind: "mutation" }), optionalMethod(LogInstanceSelectorSchema, LogReadCapabilitiesSchema, { auth: "admin" }), optionalMethod(LogReadQuerySchema, LogReadResultSchema, { auth: "admin" }), optionalMethod(object({ requestId: string().min(1).max(64) }), object({ cancelled: boolean() }), {
59145
59564
  kind: "mutation",
59146
59565
  auth: "admin"
59147
59566
  });
@@ -59245,214 +59664,6 @@ var LoginMethodContributionSchema = discriminatedUnion("kind", [
59245
59664
  ]);
59246
59665
  method(_void(), array(LoginMethodContributionSchema).readonly());
59247
59666
  /**
59248
- * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
59249
- * Apple TV, Spotify, Roku, generic OTT receivers, …) on
59250
- * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
59251
- * surface — those use `DeviceType.Camera` + the broker pipeline.
59252
- *
59253
- * The cap declares a wide method surface; not every backend supports
59254
- * every method. Drivers signal sub-capability via the
59255
- * `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
59256
- * corresponding method) so the UI gates buttons without a slice
59257
- * fetch — same pattern as `PanTiltZoom` for cameras.
59258
- */
59259
- var MediaPlayerStateSchema = _enum([
59260
- "off",
59261
- "on",
59262
- "idle",
59263
- "playing",
59264
- "paused",
59265
- "buffering",
59266
- "standby"
59267
- ]);
59268
- var MediaPlayerRepeatSchema = _enum([
59269
- "off",
59270
- "all",
59271
- "one"
59272
- ]);
59273
- var MediaInfoSchema = object({
59274
- /** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
59275
- * `podcast`, …). */
59276
- type: string(),
59277
- /** Human-readable title. */
59278
- title: string(),
59279
- /** Optional artist / channel / station label. */
59280
- artist: string().optional(),
59281
- /** Optional album / season / show name. */
59282
- album: string().optional(),
59283
- /** Optional cover-art / thumbnail URL. */
59284
- imageUrl: string().optional()
59285
- });
59286
- /**
59287
- * Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
59288
- * the device said so. `assumed`: the driver has no feedback (infrared) and
59289
- * tracks what it last SENT; it moves only after a send was accepted, and a
59290
- * human at the real remote makes it wrong. A UI shows an assumed value as a
59291
- * guess, never as a reading.
59292
- */
59293
- var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
59294
- /** The fields of a media-player status a driver can only guess. */
59295
- var MediaPlayerAssumedFieldSchema = _enum([
59296
- "state",
59297
- "volumeLevel",
59298
- "isMuted",
59299
- "source"
59300
- ]);
59301
- var MediaPlayerStatusSchema = object({
59302
- /** Playback lifecycle state. */
59303
- state: MediaPlayerStateSchema,
59304
- /**
59305
- * Whether the state fields are readings or the driver's own bookkeeping.
59306
- * A slice written before this field existed parses as `reported`.
59307
- */
59308
- stateSource: MediaPlayerStateSourceSchema.default("reported"),
59309
- /**
59310
- * Which fields are the driver's own guess. `stateSource` is `assumed` when
59311
- * this is not empty; a field not listed here is a reading even then (power
59312
- * from a running-state source, mute guessed from the last send).
59313
- */
59314
- assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
59315
- /** Volume as 0..100 inclusive. Null when the device has no volume
59316
- * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
59317
- volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59318
- /** Mute toggle distinct from volume=0. Null when no mute surface.
59319
- * Pair with `DeviceFeature.MediaPlayerMute`. */
59320
- isMuted: boolean().nullable(),
59321
- /** Active source / input. Empty when no source surface. */
59322
- source: string(),
59323
- /** Selectable sources. Empty when no source surface.
59324
- * Pair with `DeviceFeature.MediaPlayerSelectSource`. */
59325
- availableSources: array(string()),
59326
- /** Currently-playing media info. Null when nothing is playing. */
59327
- currentMedia: MediaInfoSchema.nullable(),
59328
- /** Current playback position in ms. Null when not seekable or
59329
- * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
59330
- position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59331
- /** Total duration of the current media in ms. Null when unknown
59332
- * (live stream) or nothing is playing. */
59333
- duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59334
- /** Shuffle toggle. Null when no shuffle surface.
59335
- * Pair with `DeviceFeature.MediaPlayerShuffle`. */
59336
- shuffle: boolean().nullable(),
59337
- /** Repeat mode. Null when no repeat surface.
59338
- * Pair with `DeviceFeature.MediaPlayerRepeat`. */
59339
- repeat: MediaPlayerRepeatSchema.nullable(),
59340
- /** Ms epoch when the slice was last updated. */
59341
- lastChangedAt: number()
59342
- });
59343
- var mediaPlayerCapability = {
59344
- name: "media-player",
59345
- scope: "device",
59346
- deviceNative: true,
59347
- mode: "singleton",
59348
- deviceTypes: [DeviceType.MediaPlayer],
59349
- methods: {
59350
- play: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59351
- kind: "mutation",
59352
- auth: "protected"
59353
- }),
59354
- pause: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59355
- kind: "mutation",
59356
- auth: "protected"
59357
- }),
59358
- stop: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59359
- kind: "mutation",
59360
- auth: "protected"
59361
- }),
59362
- next: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59363
- kind: "mutation",
59364
- auth: "protected"
59365
- }),
59366
- previous: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59367
- kind: "mutation",
59368
- auth: "protected"
59369
- }),
59370
- seek: method(object({
59371
- deviceId: number().int().nonnegative(),
59372
- position: number().int().nonnegative()
59373
- }), _void(), {
59374
- kind: "mutation",
59375
- auth: "protected"
59376
- }),
59377
- setVolume: method(object({
59378
- deviceId: number().int().nonnegative(),
59379
- volumeLevel: number().min(0).max(100)
59380
- }), _void(), {
59381
- kind: "mutation",
59382
- auth: "protected"
59383
- }),
59384
- setMute: method(object({
59385
- deviceId: number().int().nonnegative(),
59386
- muted: boolean()
59387
- }), _void(), {
59388
- kind: "mutation",
59389
- auth: "protected"
59390
- }),
59391
- setShuffle: method(object({
59392
- deviceId: number().int().nonnegative(),
59393
- shuffle: boolean()
59394
- }), _void(), {
59395
- kind: "mutation",
59396
- auth: "protected"
59397
- }),
59398
- setRepeat: method(object({
59399
- deviceId: number().int().nonnegative(),
59400
- repeat: MediaPlayerRepeatSchema
59401
- }), _void(), {
59402
- kind: "mutation",
59403
- auth: "protected"
59404
- }),
59405
- selectSource: method(object({
59406
- deviceId: number().int().nonnegative(),
59407
- source: string().min(1)
59408
- }), _void(), {
59409
- kind: "mutation",
59410
- auth: "protected"
59411
- }),
59412
- playMedia: method(object({
59413
- deviceId: number().int().nonnegative(),
59414
- /** Media identifier / URL. */
59415
- mediaId: string().min(1),
59416
- /** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
59417
- * passes it through to the upstream service. */
59418
- mediaType: string().min(1)
59419
- }), _void(), {
59420
- kind: "mutation",
59421
- auth: "protected"
59422
- })
59423
- },
59424
- status: {
59425
- schema: MediaPlayerStatusSchema,
59426
- kind: "push"
59427
- },
59428
- /**
59429
- * Runtime-state slice — mirrored by the kernel. UI players read the
59430
- * full slice for live now-playing, volume, and progress updates
59431
- * without polling.
59432
- */
59433
- runtimeState: MediaPlayerStatusSchema,
59434
- /**
59435
- * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
59436
- *
59437
- * See `RuntimeStateDurability`. Enforced by
59438
- * `scripts/check-runtime-state-durability.ts`.
59439
- */
59440
- durability: "session",
59441
- /**
59442
- * History (D850): volume (a level an operator or an automation sets; the
59443
- * default 10 s floor folds a slider drag into its end value) and mute.
59444
- * `position` is not recorded: it moves continuously while playing.
59445
- */
59446
- history: { fields: {
59447
- volumeLevel: {
59448
- kind: "gauge",
59449
- unit: "%",
59450
- deadband: 1
59451
- },
59452
- isMuted: { kind: "boolean" }
59453
- } }
59454
- };
59455
- /**
59456
59667
  * mesh-network — collection cap for mesh-VPN providers.
59457
59668
  *
59458
59669
  * Different shape from `network-access` (public tunnels):
@@ -73536,6 +73747,14 @@ var powerMeterCapability = {
73536
73747
  deviceNative: true,
73537
73748
  mode: "singleton",
73538
73749
  deviceTypes: POWER_METER_DEVICE_TYPES,
73750
+ deviceConfig: { ui: {
73751
+ kind: "widget",
73752
+ widgetId: "host/power-meter-line",
73753
+ tab: "power",
73754
+ placement: "hero",
73755
+ label: "Power",
73756
+ order: 4
73757
+ } },
73539
73758
  methods: {},
73540
73759
  status: {
73541
73760
  schema: PowerMeterStatusSchema,
@@ -73595,6 +73814,117 @@ var powerMeterCapability = {
73595
73814
  volatileStateFields: ["lastFetchedAt"],
73596
73815
  customize: powerMeterCustomize
73597
73816
  };
73817
+ var PowerSourceRefusalReasonSchema = _enum([
73818
+ "no-power-source",
73819
+ "switch-missing",
73820
+ "switch-offline",
73821
+ "switch-unknown",
73822
+ "restart-in-progress",
73823
+ "switch-call-failed"
73824
+ ]);
73825
+ var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
73826
+ accepted: literal(false),
73827
+ reason: PowerSourceRefusalReasonSchema,
73828
+ /** One line for the operator, naming the switch. */
73829
+ message: string()
73830
+ })]);
73831
+ var PowerSourceInfoSchema = object({
73832
+ /** False when the device has no power source linked; the rest is then empty. */
73833
+ linked: boolean(),
73834
+ switchDeviceId: number().nullable(),
73835
+ switchName: string().nullable(),
73836
+ /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
73837
+ switchOn: boolean().nullable(),
73838
+ switchOnline: boolean().nullable(),
73839
+ restartInProgress: boolean(),
73840
+ offSeconds: number().int()
73841
+ });
73842
+ /** The composed slice: the switch state the link mirrors, plus the stored off time. */
73843
+ var PowerSourceStateSchema = object({
73844
+ /** Null while the switch has not reported: unknown is never `false` (D393). */
73845
+ switchOn: boolean().nullable(),
73846
+ offSeconds: number().int(),
73847
+ lastFetchedAt: number()
73848
+ });
73849
+ var POWER_SOURCE_DEVICE_TYPES = [
73850
+ DeviceType.Gateway,
73851
+ DeviceType.Hub,
73852
+ DeviceType.Camera,
73853
+ DeviceType.Sensor,
73854
+ DeviceType.MediaPlayer,
73855
+ DeviceType.Vacuum,
73856
+ DeviceType.Generic,
73857
+ DeviceType.Washer,
73858
+ DeviceType.Dryer,
73859
+ DeviceType.Dishwasher,
73860
+ DeviceType.PetFountain,
73861
+ DeviceType.Fan,
73862
+ DeviceType.Humidifier,
73863
+ DeviceType.Climate,
73864
+ DeviceType.WaterHeater
73865
+ ];
73866
+ var powerSourceCustomize = {
73867
+ version: 1,
73868
+ targets: [{
73869
+ id: "switch",
73870
+ cardinality: "scalar",
73871
+ vocabulary: {
73872
+ cap: "power-source",
73873
+ stateField: "switchOn"
73874
+ },
73875
+ deviceTypes: POWER_SOURCE_DEVICE_TYPES,
73876
+ apply: "add",
73877
+ path: "switchOn",
73878
+ sources: [{
73879
+ kind: "switch",
73880
+ transforms: [{ kind: "identity" }]
73881
+ }],
73882
+ companions: [{
73883
+ kind: "constant",
73884
+ path: "offSeconds",
73885
+ value: 10,
73886
+ when: "add"
73887
+ }, {
73888
+ kind: "clock",
73889
+ path: "lastFetchedAt",
73890
+ when: "always"
73891
+ }],
73892
+ sourceReport: { status: "assignment-status" }
73893
+ }]
73894
+ };
73895
+ var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
73896
+ var powerSourceCapability = {
73897
+ name: "power-source",
73898
+ scope: "device",
73899
+ mode: "singleton",
73900
+ deviceTypes: POWER_SOURCE_DEVICE_TYPES,
73901
+ methods: {
73902
+ getStatus: systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "admin" }),
73903
+ powerOn: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73904
+ kind: "mutation",
73905
+ auth: "admin"
73906
+ }),
73907
+ powerOff: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73908
+ kind: "mutation",
73909
+ auth: "admin"
73910
+ }),
73911
+ forceRestart: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73912
+ kind: "mutation",
73913
+ auth: "admin"
73914
+ })
73915
+ },
73916
+ runtimeState: PowerSourceStateSchema,
73917
+ customize: powerSourceCustomize,
73918
+ /**
73919
+ * Runtime-state durability: **session** — mirrored from the linked switch on
73920
+ * its next report; a restored value would outlive the reading it names.
73921
+ *
73922
+ * See `RuntimeStateDurability`. Enforced by
73923
+ * `scripts/check-runtime-state-durability.ts`.
73924
+ */
73925
+ durability: "session",
73926
+ history: "none"
73927
+ };
73598
73928
  /**
73599
73929
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
73600
73930
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -77129,7 +77459,13 @@ var RemoteMediaCommandSchema = _enum([
77129
77459
  "next",
77130
77460
  "previous",
77131
77461
  "setMute",
77132
- "selectSource"
77462
+ "selectSource",
77463
+ "turnOn",
77464
+ "turnOff",
77465
+ "volumeUp",
77466
+ "volumeDown",
77467
+ "channelUp",
77468
+ "channelDown"
77133
77469
  ]);
77134
77470
  /** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
77135
77471
  var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
@@ -77150,7 +77486,7 @@ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.e
77150
77486
  }), RemoteSendResultSchema, {
77151
77487
  kind: "mutation",
77152
77488
  auth: "protected"
77153
- }), method(deviceInput.extend({
77489
+ }), method(deviceInput, MediaPlayerStatusSchema.nullable(), { auth: "protected" }), method(deviceInput.extend({
77154
77490
  command: RemoteMediaCommandSchema,
77155
77491
  muted: boolean().optional(),
77156
77492
  source: string().optional(),
@@ -79434,7 +79770,11 @@ listSignals: method(_void(), array(StreamSignalSchema).readonly()) }
79434
79770
  * `getSettingsUISchema()` surface.
79435
79771
  */
79436
79772
  var SwitchStatusSchema = object({
79437
- on: boolean().meta({ label: "On" }),
79773
+ /**
79774
+ * `null` = the device has never reported its state (a measurement that
79775
+ * has not happened is unknown, never `false`).
79776
+ */
79777
+ on: boolean().nullable().meta({ label: "On" }),
79438
79778
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
79439
79779
  lastChangedAt: number()
79440
79780
  });
@@ -79459,7 +79799,7 @@ var switchCapability = {
79459
79799
  }) },
79460
79800
  events: { onStateChanged: { data: object({
79461
79801
  deviceId: number(),
79462
- on: boolean(),
79802
+ on: boolean().nullable(),
79463
79803
  lastChangedAt: number()
79464
79804
  }) } },
79465
79805
  status: {
@@ -83062,6 +83402,26 @@ function buildEventKindDescriptor(kind, source) {
83062
83402
  }
83063
83403
  };
83064
83404
  }
83405
+ /**
83406
+ * What a remote key is CALLED and what it LOOKS like, for every role (D963),
83407
+ * shared by the admin UI and the viewer. A role is data on the wire; each
83408
+ * client resolves its name from `label` (English and Italian side by side) and
83409
+ * its glyph from `icon`, a lucide id from the shared icon catalogue — the
83410
+ * server sends an id, a client maps it to its own icon set.
83411
+ *
83412
+ * `legend` is text printed on the key INSTEAD of a glyph (a digit has no
83413
+ * pictogram): a client that can draw text shows it, one that cannot falls back
83414
+ * to `icon`.
83415
+ */
83416
+ function digit(n) {
83417
+ return {
83418
+ label: t(n, n),
83419
+ icon: "hash",
83420
+ legend: n
83421
+ };
83422
+ }
83423
+ digit("0"), digit("1"), digit("2"), digit("3"), digit("4"), digit("5"), digit("6"), digit("7"), digit("8"), digit("9");
83424
+ DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
83065
83425
  var btn = (idSuffix, row, col, init) => ({
83066
83426
  idSuffix,
83067
83427
  row,
@@ -83188,6 +83548,42 @@ discriminatedUnion("kind", [object({
83188
83548
  kind: literal("pronto"),
83189
83549
  code: string()
83190
83550
  })]);
83551
+ /**
83552
+ * Propose a running threshold from a device's own power history (planning#405).
83553
+ *
83554
+ * Pure and deterministic: the same series gives the same answer. The series is
83555
+ * a HELD-VALUE step series — a point's value stands until the next point — which
83556
+ * is how device-history stores a gauge, so "time at a level" is exact, not a
83557
+ * count of samples.
83558
+ *
83559
+ * 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
83560
+ * near F. A device that is not near one level most of the time has no floor.
83561
+ * 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
83562
+ * 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
83563
+ * display waking): it is neither a run nor a pause. Among the remaining
83564
+ * episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
83565
+ * two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
83566
+ * a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
83567
+ * is no run.
83568
+ * 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
83569
+ * grid a person would type; stop dwell covers the longest pause and the
83570
+ * kind's floor; start dwell covers the longest pulse and standby blip above T.
83571
+ * 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
83572
+ * run must hold exactly one true interval and nothing may flap outside.
83573
+ */
83574
+ var SECOND_MS = 1e3;
83575
+ var MINUTE_MS$1 = 60 * SECOND_MS;
83576
+ 24 * (60 * MINUTE_MS$1);
83577
+ 10 * MINUTE_MS$1;
83578
+ 30 * SECOND_MS;
83579
+ 60 * SECOND_MS;
83580
+ 20 * MINUTE_MS$1;
83581
+ 90 * SECOND_MS;
83582
+ 30 * MINUTE_MS$1;
83583
+ 10 * MINUTE_MS$1;
83584
+ DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
83585
+ 10 * SECOND_MS;
83586
+ 10 * SECOND_MS;
83191
83587
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
83192
83588
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
83193
83589
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
@@ -85852,6 +86248,13 @@ Object.freeze({
85852
86248
  access: "create",
85853
86249
  role: "admin"
85854
86250
  },
86251
+ "coreBlocks.proposeRunningThreshold": {
86252
+ capName: "core-blocks",
86253
+ capScope: "system",
86254
+ addonId: null,
86255
+ access: "view",
86256
+ role: "admin"
86257
+ },
85855
86258
  "coreBlocks.reportAutomationRun": {
85856
86259
  capName: "core-blocks",
85857
86260
  capScope: "system",
@@ -86363,6 +86766,13 @@ Object.freeze({
86363
86766
  access: "view",
86364
86767
  role: "admin"
86365
86768
  },
86769
+ "deviceExport.listInstances": {
86770
+ capName: "device-export",
86771
+ capScope: "system",
86772
+ addonId: null,
86773
+ access: "view",
86774
+ role: "admin"
86775
+ },
86366
86776
  "deviceExport.listSupportedDeviceKinds": {
86367
86777
  capName: "device-export",
86368
86778
  capScope: "system",
@@ -88015,6 +88425,13 @@ Object.freeze({
88015
88425
  access: "create",
88016
88426
  role: "admin"
88017
88427
  },
88428
+ "integrations.clearSettings": {
88429
+ capName: "integrations",
88430
+ capScope: "system",
88431
+ addonId: null,
88432
+ access: "delete",
88433
+ role: "admin"
88434
+ },
88018
88435
  "integrations.create": {
88019
88436
  capName: "integrations",
88020
88437
  capScope: "system",
@@ -88799,6 +89216,20 @@ Object.freeze({
88799
89216
  access: "view",
88800
89217
  role: "admin"
88801
89218
  },
89219
+ "mediaPlayer.channelDown": {
89220
+ capName: "media-player",
89221
+ capScope: "device",
89222
+ addonId: null,
89223
+ access: "create",
89224
+ role: "viewer"
89225
+ },
89226
+ "mediaPlayer.channelUp": {
89227
+ capName: "media-player",
89228
+ capScope: "device",
89229
+ addonId: null,
89230
+ access: "create",
89231
+ role: "viewer"
89232
+ },
88802
89233
  "mediaPlayer.getStatus": {
88803
89234
  capName: "media-player",
88804
89235
  capScope: "device",
@@ -88890,6 +89321,34 @@ Object.freeze({
88890
89321
  access: "create",
88891
89322
  role: "viewer"
88892
89323
  },
89324
+ "mediaPlayer.turnOff": {
89325
+ capName: "media-player",
89326
+ capScope: "device",
89327
+ addonId: null,
89328
+ access: "create",
89329
+ role: "viewer"
89330
+ },
89331
+ "mediaPlayer.turnOn": {
89332
+ capName: "media-player",
89333
+ capScope: "device",
89334
+ addonId: null,
89335
+ access: "create",
89336
+ role: "viewer"
89337
+ },
89338
+ "mediaPlayer.volumeDown": {
89339
+ capName: "media-player",
89340
+ capScope: "device",
89341
+ addonId: null,
89342
+ access: "create",
89343
+ role: "viewer"
89344
+ },
89345
+ "mediaPlayer.volumeUp": {
89346
+ capName: "media-player",
89347
+ capScope: "device",
89348
+ addonId: null,
89349
+ access: "create",
89350
+ role: "viewer"
89351
+ },
88893
89352
  "meshNetwork.getStatus": {
88894
89353
  capName: "mesh-network",
88895
89354
  capScope: "system",
@@ -92446,6 +92905,13 @@ Object.freeze({
92446
92905
  access: "create",
92447
92906
  role: "admin"
92448
92907
  },
92908
+ "remote.getAssumedStatus": {
92909
+ capName: "remote",
92910
+ capScope: "system",
92911
+ addonId: null,
92912
+ access: "view",
92913
+ role: "admin"
92914
+ },
92449
92915
  "remote.getConfig": {
92450
92916
  capName: "remote",
92451
92917
  capScope: "system",
@@ -95261,6 +95727,11 @@ Object.freeze({
95261
95727
  form: "single",
95262
95728
  optional: false
95263
95729
  }],
95730
+ "coreBlocks.proposeRunningThreshold": [{
95731
+ name: "deviceId",
95732
+ form: "single",
95733
+ optional: false
95734
+ }],
95264
95735
  "cover.close": [{
95265
95736
  name: "deviceId",
95266
95737
  form: "single",
@@ -96112,6 +96583,16 @@ Object.freeze({
96112
96583
  form: "single",
96113
96584
  optional: true
96114
96585
  }],
96586
+ "mediaPlayer.channelDown": [{
96587
+ name: "deviceId",
96588
+ form: "single",
96589
+ optional: false
96590
+ }],
96591
+ "mediaPlayer.channelUp": [{
96592
+ name: "deviceId",
96593
+ form: "single",
96594
+ optional: false
96595
+ }],
96115
96596
  "mediaPlayer.getStatus": [{
96116
96597
  name: "deviceId",
96117
96598
  form: "single",
@@ -96177,6 +96658,26 @@ Object.freeze({
96177
96658
  form: "single",
96178
96659
  optional: false
96179
96660
  }],
96661
+ "mediaPlayer.turnOff": [{
96662
+ name: "deviceId",
96663
+ form: "single",
96664
+ optional: false
96665
+ }],
96666
+ "mediaPlayer.turnOn": [{
96667
+ name: "deviceId",
96668
+ form: "single",
96669
+ optional: false
96670
+ }],
96671
+ "mediaPlayer.volumeDown": [{
96672
+ name: "deviceId",
96673
+ form: "single",
96674
+ optional: false
96675
+ }],
96676
+ "mediaPlayer.volumeUp": [{
96677
+ name: "deviceId",
96678
+ form: "single",
96679
+ optional: false
96680
+ }],
96180
96681
  "meter.getStatus": [{
96181
96682
  name: "deviceId",
96182
96683
  form: "single",
@@ -97342,6 +97843,11 @@ Object.freeze({
97342
97843
  form: "single",
97343
97844
  optional: true
97344
97845
  }],
97846
+ "remote.getAssumedStatus": [{
97847
+ name: "deviceId",
97848
+ form: "single",
97849
+ optional: false
97850
+ }],
97345
97851
  "remote.getConfig": [{
97346
97852
  name: "deviceId",
97347
97853
  form: "single",
@@ -97986,99 +98492,6 @@ var BucketAccumulator = class {
97986
98492
  return n;
97987
98493
  }
97988
98494
  };
97989
- /**
97990
- * A saved Customize assignment (D936). The schema is the type: callers use
97991
- * `z.infer`, they do not restate the shape.
97992
- */
97993
- var OverrideSourceSchema = object({
97994
- addonId: string().min(1),
97995
- stableId: string().min(1),
97996
- cap: string().min(1),
97997
- fieldPath: string().min(1)
97998
- });
97999
- var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
98000
- object({ kind: literal("identity") }),
98001
- object({ kind: literal("invert") }),
98002
- object({
98003
- kind: literal("threshold"),
98004
- comparison: _enum(["gte", "lte"]),
98005
- threshold: number(),
98006
- startDwellMs: number().nonnegative(),
98007
- stopDwellMs: number().nonnegative()
98008
- }),
98009
- object({
98010
- kind: literal("linear"),
98011
- scale: number(),
98012
- offset: number(),
98013
- clampMin: number().optional(),
98014
- clampMax: number().optional()
98015
- }),
98016
- object({
98017
- kind: literal("enum-map"),
98018
- mapping: record(string(), string().nullable())
98019
- }),
98020
- object({ kind: literal("number") })
98021
- ]);
98022
- var ScalarOverrideDraftSchema = object({
98023
- kind: literal("scalar"),
98024
- cap: string().min(1),
98025
- targetId: string().min(1),
98026
- source: OverrideSourceSchema,
98027
- transform: CustomizeTransformDraftSchema
98028
- });
98029
- var KeyedArrayOverrideDraftSchema = object({
98030
- kind: literal("keyedArray"),
98031
- cap: string().min(1),
98032
- targetId: string().min(1),
98033
- key: string().min(1),
98034
- label: string().min(1),
98035
- fieldPath: string().min(1),
98036
- source: OverrideSourceSchema,
98037
- transform: CustomizeTransformDraftSchema
98038
- });
98039
- var PulseOverrideDraftSchema = object({
98040
- kind: literal("pulse"),
98041
- cap: string().min(1),
98042
- targetId: string().min(1),
98043
- enabled: boolean(),
98044
- debounceMs: number().nonnegative(),
98045
- edges: array(_enum([
98046
- "rising",
98047
- "button",
98048
- "native-event"
98049
- ])).min(1).optional(),
98050
- sources: array(OverrideSourceSchema).min(1)
98051
- });
98052
- var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
98053
- "in-addition",
98054
- "confirm",
98055
- "replace"
98056
- ]).optional() });
98057
- discriminatedUnion("kind", [
98058
- ScalarOverrideDraftSchema,
98059
- KeyedArrayOverrideDraftSchema,
98060
- PulseOverrideDraftSchema,
98061
- object({
98062
- kind: literal("span"),
98063
- cap: string().min(1),
98064
- targetId: string().min(1),
98065
- timeoutMs: number().nonnegative(),
98066
- cooldownMs: number().nonnegative().optional(),
98067
- sources: array(SpanSourceDraftSchema)
98068
- })
98069
- ]);
98070
- object({
98071
- targetId: string().min(1),
98072
- configured: boolean(),
98073
- source: _enum(["native", "assigned"]),
98074
- state: _enum([
98075
- "active",
98076
- "stale",
98077
- "released",
98078
- "unavailable"
98079
- ]),
98080
- reason: string().nullable()
98081
- });
98082
98495
  var G711_SCALE_CORRECTION_DB = {
98083
98496
  PCMU: 20 * Math.log10(4),
98084
98497
  PCMA: 20 * Math.log10(8)