@camstack/addon-post-analysis 1.2.665 → 1.2.667

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 +21 -0
  2. package/dist/{clip-model-registry-nn_STiGt.js → clip-model-registry-D7cqhFK3.js} +758 -486
  3. package/dist/{clip-model-registry-DZTzG2p4.mjs → clip-model-registry-DpzRS8q3.mjs} +758 -486
  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-Hy74pMMo.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DmTIEp0m.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-DAAkZRnK.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-znrgUrN1.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit-BAYLsfoZ.mjs → hostInit-ODebWXu_.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +563 -122
  12. package/dist/pipeline-analytics/index.mjs +563 -122
  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-BXTAZYaJ.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-hmSP-eJt.mjs
6072
+ //#region ../types/dist/sleep-hK-Dss3z.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).
@@ -6142,6 +6148,22 @@ function buildAlawTable() {
6142
6148
  }
6143
6149
  buildUlawTable();
6144
6150
  buildAlawTable();
6151
+ /**
6152
+ * An addon whose own migrations did not apply refuses to initialise (D987). The error is typed
6153
+ * so a runner hosting several addons can tell "this addon is blocked" from a crash and keep its
6154
+ * co-located siblings running. Recognised by NAME and shape, never `instanceof`: a forked addon
6155
+ * bundles its own copy of this module, so the class the runner sees is not the one that threw.
6156
+ */
6157
+ var MIGRATIONS_BLOCKED_ERROR_NAME = "AddonMigrationsBlockedError";
6158
+ var AddonMigrationsBlockedError = class extends Error {
6159
+ /** The ledger ids that did not apply and block the owner. */
6160
+ migrationIds;
6161
+ constructor(migrationIds) {
6162
+ super(`migration(s) ${migrationIds.join(", ")} did not apply and block this addon — fix or retry them (migrations.retry), then restart it`);
6163
+ this.name = MIGRATIONS_BLOCKED_ERROR_NAME;
6164
+ this.migrationIds = migrationIds;
6165
+ }
6166
+ };
6145
6167
  /** Is this field's stored value a credential? */
6146
6168
  function isSecretConfigField(field) {
6147
6169
  if (field.type === "password") return true;
@@ -7795,7 +7817,7 @@ var BaseAddon = class {
7795
7817
  startMigration: (input) => api.startMigration.mutate(input),
7796
7818
  finishMigration: (input) => api.finishMigration.mutate(input)
7797
7819
  }, migrations, this.ctx.logger);
7798
- if (report.blockOwner.length > 0) throw new Error(`migration(s) ${report.blockOwner.join(", ")} did not apply and block this addon — fix or retry them (migrations.retry), then restart it`);
7820
+ if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
7799
7821
  return report;
7800
7822
  }
7801
7823
  /**
@@ -8135,6 +8157,10 @@ var BaseAddon = class {
8135
8157
  const stored = keychain ? await this.readAddonStoreWithRetry() : {};
8136
8158
  const patch = {};
8137
8159
  for (const key of keys) {
8160
+ if (tree.get(key)?.kind === "array") {
8161
+ patch[key] = [];
8162
+ continue;
8163
+ }
8138
8164
  const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
8139
8165
  patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
8140
8166
  }
@@ -11983,7 +12009,7 @@ function sleep(ms) {
11983
12009
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11984
12010
  }
11985
12011
  //#endregion
11986
- //#region ../types/dist/composition-Dh3RfMx5.mjs
12012
+ //#region ../types/dist/composition-BSGHhcr8.mjs
11987
12013
  /**
11988
12014
  * Deterministic SHA-256 hash of an arbitrary serialisable value. The
11989
12015
  * canonical form sorts object keys alphabetically at every depth so two
@@ -12906,38 +12932,6 @@ function validateExpressionSource(src) {
12906
12932
  return null;
12907
12933
  }
12908
12934
  /**
12909
- * One power reading becomes a washer, dryer or dishwasher (D944 helper).
12910
- *
12911
- * Standby of the control board is under 5 W; a motor or a heater is far
12912
- * above that, so 5 W is the line. A door lock or a short pump spike is not
12913
- * a cycle, so running starts only after 30 s above the line.
12914
- *
12915
- * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
12916
- * bursts) lasts several minutes. Ending the cycle on the first dip would
12917
- * report "finished" in the middle of the program, so the stop dwell is
12918
- * 10 minutes on every type. The number is stored per type so one machine
12919
- * can move later without moving the others.
12920
- */
12921
- var APPLIANCE_TEMPLATE_ID = "appliance";
12922
- var APPLIANCE_KINDS = [
12923
- "washer",
12924
- "dryer",
12925
- "dishwasher"
12926
- ];
12927
- DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
12928
- /**
12929
- * A dumb plug-in stove or heater, built from a smart plug (a `Heater`).
12930
- *
12931
- * The plug is the actuator: `switch.setState` is a FORWARD to the plug's own
12932
- * (D967), so the heater switches exactly as the plug does. Running is the
12933
- * plug being on; when a power reading is also named, running is the draw
12934
- * staying above the line instead (a plug left on with the stove's own
12935
- * thermostat open is not heating). 50 W is far above any standby and below
12936
- * the smallest radiator.
12937
- */
12938
- var HEATER_TEMPLATE_ID = "heater";
12939
- DeviceType.Heater;
12940
- /**
12941
12935
  * Statistics and integrals over a history read (D944).
12942
12936
  *
12943
12937
  * Pure: the same points always give the same answer, so a restart recomputes
@@ -13081,34 +13075,6 @@ var HelperSpecSchema = discriminatedUnion("kind", [
13081
13075
  /** Actuator groups only. Readings leave this unset. */
13082
13076
  active: ActiveCriterionSchema.optional()
13083
13077
  }),
13084
- object({
13085
- kind: literal("appliance"),
13086
- templateId: literal(APPLIANCE_TEMPLATE_ID),
13087
- appliance: _enum(APPLIANCE_KINDS),
13088
- thresholdWatts: number().positive(),
13089
- startDwellMs: number().int().nonnegative(),
13090
- stopDwellMs: number().int().nonnegative(),
13091
- member: HelperMemberSchema,
13092
- /**
13093
- * The plug that powers the appliance (`switch.on`). Optional: when set, the
13094
- * composition carries a `power-source` feature, so the hub can cut and restore
13095
- * that plug (power-source link of a NEW-target composition).
13096
- */
13097
- powerSwitch: HelperMemberSchema.optional(),
13098
- /** How long a forced restart keeps the plug off; the cap's default when unset. */
13099
- offSeconds: number().optional()
13100
- }),
13101
- object({
13102
- kind: literal("heater"),
13103
- templateId: literal(HEATER_TEMPLATE_ID),
13104
- /** The plug, read and commanded at `switch.on`. */
13105
- plug: HelperMemberSchema,
13106
- /** The plug's power reading, when it has one: running is then the draw, not the switch. */
13107
- meter: HelperMemberSchema.optional(),
13108
- thresholdWatts: number().positive(),
13109
- startDwellMs: number().int().nonnegative(),
13110
- stopDwellMs: number().int().nonnegative()
13111
- }),
13112
13078
  object({
13113
13079
  kind: literal("statistic"),
13114
13080
  stat: _enum(STATISTIC_STATS),
@@ -13280,7 +13246,14 @@ var CompositionForwardCommandSchema = object({
13280
13246
  method: string().min(1),
13281
13247
  args: record(string().min(1), CompositionForwardArgSchema).optional()
13282
13248
  });
13283
- var CompositionCommandTargetSchema = discriminatedUnion("kind", [CompositionForwardCommandSchema, CompositionCodeSourceSchema]);
13249
+ var CompositionCommandTargetSchema = discriminatedUnion("kind", [
13250
+ CompositionForwardCommandSchema,
13251
+ CompositionCodeSourceSchema,
13252
+ object({
13253
+ kind: literal("extension"),
13254
+ target: literal("remote")
13255
+ })
13256
+ ]);
13284
13257
  /** An item key inside an item-array cap (`consumables.items[<key>]`). */
13285
13258
  var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
13286
13259
  /**
@@ -13325,7 +13298,7 @@ var CompositionSchema = object({
13325
13298
  kind: literal("existing"),
13326
13299
  device: CompositionSourceRefSchema
13327
13300
  })]),
13328
- features: array(CompositionFeatureSchema).min(1).max(16),
13301
+ features: array(CompositionFeatureSchema).min(0).max(16),
13329
13302
  /**
13330
13303
  * Present when the block is a helper template. Absent on a free
13331
13304
  * composition (the room example, device 4603). The editor reads this,
@@ -20547,6 +20520,48 @@ var APPLIANCE_CAP_VOCABULARY = {
20547
20520
  scope: "device",
20548
20521
  noun: t("Media player", "Lettore multimediale"),
20549
20522
  actions: [
20523
+ {
20524
+ id: "media.turnOn",
20525
+ method: "turnOn",
20526
+ label: t("Turn on", "Accendi"),
20527
+ phrase: t("Turn on {device}", "Accendi {device}"),
20528
+ icon: "power"
20529
+ },
20530
+ {
20531
+ id: "media.turnOff",
20532
+ method: "turnOff",
20533
+ label: t("Turn off", "Spegni"),
20534
+ phrase: t("Turn off {device}", "Spegni {device}"),
20535
+ icon: "power"
20536
+ },
20537
+ {
20538
+ id: "media.volumeUp",
20539
+ method: "volumeUp",
20540
+ label: t("Volume up", "Alza il volume"),
20541
+ phrase: t("Raise the volume of {device}", "Alza il volume di {device}"),
20542
+ icon: "volume-2"
20543
+ },
20544
+ {
20545
+ id: "media.volumeDown",
20546
+ method: "volumeDown",
20547
+ label: t("Volume down", "Abbassa il volume"),
20548
+ phrase: t("Lower the volume of {device}", "Abbassa il volume di {device}"),
20549
+ icon: "volume-2"
20550
+ },
20551
+ {
20552
+ id: "media.channelUp",
20553
+ method: "channelUp",
20554
+ label: t("Channel up", "Canale successivo"),
20555
+ phrase: t("Go to the next channel on {device}", "Passa al canale successivo su {device}"),
20556
+ icon: "skip-forward"
20557
+ },
20558
+ {
20559
+ id: "media.channelDown",
20560
+ method: "channelDown",
20561
+ label: t("Channel down", "Canale precedente"),
20562
+ phrase: t("Go to the previous channel on {device}", "Torna al canale precedente su {device}"),
20563
+ icon: "skip-back"
20564
+ },
20550
20565
  {
20551
20566
  id: "media.play",
20552
20567
  method: "play",
@@ -50349,6 +50364,244 @@ var controlCapability = {
50349
50364
  */
50350
50365
  history: "none"
50351
50366
  };
50367
+ /**
50368
+ * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
50369
+ * Apple TV, Spotify, Roku, generic OTT receivers, …) on
50370
+ * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
50371
+ * surface — those use `DeviceType.Camera` + the broker pipeline.
50372
+ *
50373
+ * The cap declares a wide method surface; not every backend supports
50374
+ * every method. Drivers signal sub-capability via the
50375
+ * `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
50376
+ * corresponding method) so the UI gates buttons without a slice
50377
+ * fetch — same pattern as `PanTiltZoom` for cameras.
50378
+ */
50379
+ var MediaPlayerStateSchema = _enum([
50380
+ "off",
50381
+ "on",
50382
+ "idle",
50383
+ "playing",
50384
+ "paused",
50385
+ "buffering",
50386
+ "standby"
50387
+ ]);
50388
+ var MediaPlayerRepeatSchema = _enum([
50389
+ "off",
50390
+ "all",
50391
+ "one"
50392
+ ]);
50393
+ var MediaInfoSchema = object({
50394
+ /** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
50395
+ * `podcast`, …). */
50396
+ type: string(),
50397
+ /** Human-readable title. */
50398
+ title: string(),
50399
+ /** Optional artist / channel / station label. */
50400
+ artist: string().optional(),
50401
+ /** Optional album / season / show name. */
50402
+ album: string().optional(),
50403
+ /** Optional cover-art / thumbnail URL. */
50404
+ imageUrl: string().optional()
50405
+ });
50406
+ /**
50407
+ * Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
50408
+ * the device said so. `assumed`: the driver has no feedback (infrared) and
50409
+ * tracks what it last SENT; it moves only after a send was accepted, and a
50410
+ * human at the real remote makes it wrong. A UI shows an assumed value as a
50411
+ * guess, never as a reading.
50412
+ */
50413
+ var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
50414
+ /** The fields of a media-player status a driver can only guess. */
50415
+ var MediaPlayerAssumedFieldSchema = _enum([
50416
+ "state",
50417
+ "volumeLevel",
50418
+ "isMuted",
50419
+ "source"
50420
+ ]);
50421
+ var MediaPlayerStatusSchema = object({
50422
+ /** Playback lifecycle state. */
50423
+ state: MediaPlayerStateSchema,
50424
+ /**
50425
+ * Whether the state fields are readings or the driver's own bookkeeping.
50426
+ * A slice written before this field existed parses as `reported`.
50427
+ */
50428
+ stateSource: MediaPlayerStateSourceSchema.default("reported"),
50429
+ /**
50430
+ * Which fields are the driver's own guess. `stateSource` is `assumed` when
50431
+ * this is not empty; a field not listed here is a reading even then (power
50432
+ * from a running-state source, mute guessed from the last send).
50433
+ */
50434
+ assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
50435
+ /** Volume as 0..100 inclusive. Null when the device has no volume
50436
+ * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
50437
+ volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
50438
+ /** Mute toggle distinct from volume=0. Null when no mute surface.
50439
+ * Pair with `DeviceFeature.MediaPlayerMute`. */
50440
+ isMuted: boolean().nullable(),
50441
+ /** Active source / input. Empty when no source surface. */
50442
+ source: string(),
50443
+ /** Selectable sources. Empty when no source surface.
50444
+ * Pair with `DeviceFeature.MediaPlayerSelectSource`. */
50445
+ availableSources: array(string()),
50446
+ /** Currently-playing media info. Null when nothing is playing. */
50447
+ currentMedia: MediaInfoSchema.nullable(),
50448
+ /** Current playback position in ms. Null when not seekable or
50449
+ * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
50450
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
50451
+ /** Total duration of the current media in ms. Null when unknown
50452
+ * (live stream) or nothing is playing. */
50453
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
50454
+ /** Shuffle toggle. Null when no shuffle surface.
50455
+ * Pair with `DeviceFeature.MediaPlayerShuffle`. */
50456
+ shuffle: boolean().nullable(),
50457
+ /** Repeat mode. Null when no repeat surface.
50458
+ * Pair with `DeviceFeature.MediaPlayerRepeat`. */
50459
+ repeat: MediaPlayerRepeatSchema.nullable(),
50460
+ /** Ms epoch when the slice was last updated. */
50461
+ lastChangedAt: number()
50462
+ });
50463
+ var stepInput = object({
50464
+ deviceId: number().int().nonnegative(),
50465
+ /** How many presses: the caller's count (Alexa `volumeSteps`, `channelCount`). */
50466
+ steps: number().int().min(1).max(50).default(1)
50467
+ });
50468
+ var deviceOnly = object({ deviceId: number().int().nonnegative() });
50469
+ var mediaPlayerCapability = {
50470
+ name: "media-player",
50471
+ scope: "device",
50472
+ deviceNative: true,
50473
+ mode: "singleton",
50474
+ deviceTypes: [DeviceType.MediaPlayer],
50475
+ methods: {
50476
+ turnOn: method(deviceOnly, _void(), {
50477
+ kind: "mutation",
50478
+ auth: "protected"
50479
+ }),
50480
+ turnOff: method(deviceOnly, _void(), {
50481
+ kind: "mutation",
50482
+ auth: "protected"
50483
+ }),
50484
+ volumeUp: method(stepInput, _void(), {
50485
+ kind: "mutation",
50486
+ auth: "protected"
50487
+ }),
50488
+ volumeDown: method(stepInput, _void(), {
50489
+ kind: "mutation",
50490
+ auth: "protected"
50491
+ }),
50492
+ channelUp: method(stepInput, _void(), {
50493
+ kind: "mutation",
50494
+ auth: "protected"
50495
+ }),
50496
+ channelDown: method(stepInput, _void(), {
50497
+ kind: "mutation",
50498
+ auth: "protected"
50499
+ }),
50500
+ play: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50501
+ kind: "mutation",
50502
+ auth: "protected"
50503
+ }),
50504
+ pause: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50505
+ kind: "mutation",
50506
+ auth: "protected"
50507
+ }),
50508
+ stop: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50509
+ kind: "mutation",
50510
+ auth: "protected"
50511
+ }),
50512
+ next: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50513
+ kind: "mutation",
50514
+ auth: "protected"
50515
+ }),
50516
+ previous: method(object({ deviceId: number().int().nonnegative() }), _void(), {
50517
+ kind: "mutation",
50518
+ auth: "protected"
50519
+ }),
50520
+ seek: method(object({
50521
+ deviceId: number().int().nonnegative(),
50522
+ position: number().int().nonnegative()
50523
+ }), _void(), {
50524
+ kind: "mutation",
50525
+ auth: "protected"
50526
+ }),
50527
+ setVolume: method(object({
50528
+ deviceId: number().int().nonnegative(),
50529
+ volumeLevel: number().min(0).max(100)
50530
+ }), _void(), {
50531
+ kind: "mutation",
50532
+ auth: "protected"
50533
+ }),
50534
+ setMute: method(object({
50535
+ deviceId: number().int().nonnegative(),
50536
+ muted: boolean()
50537
+ }), _void(), {
50538
+ kind: "mutation",
50539
+ auth: "protected"
50540
+ }),
50541
+ setShuffle: method(object({
50542
+ deviceId: number().int().nonnegative(),
50543
+ shuffle: boolean()
50544
+ }), _void(), {
50545
+ kind: "mutation",
50546
+ auth: "protected"
50547
+ }),
50548
+ setRepeat: method(object({
50549
+ deviceId: number().int().nonnegative(),
50550
+ repeat: MediaPlayerRepeatSchema
50551
+ }), _void(), {
50552
+ kind: "mutation",
50553
+ auth: "protected"
50554
+ }),
50555
+ selectSource: method(object({
50556
+ deviceId: number().int().nonnegative(),
50557
+ source: string().min(1)
50558
+ }), _void(), {
50559
+ kind: "mutation",
50560
+ auth: "protected"
50561
+ }),
50562
+ playMedia: method(object({
50563
+ deviceId: number().int().nonnegative(),
50564
+ /** Media identifier / URL. */
50565
+ mediaId: string().min(1),
50566
+ /** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
50567
+ * passes it through to the upstream service. */
50568
+ mediaType: string().min(1)
50569
+ }), _void(), {
50570
+ kind: "mutation",
50571
+ auth: "protected"
50572
+ })
50573
+ },
50574
+ status: {
50575
+ schema: MediaPlayerStatusSchema,
50576
+ kind: "push"
50577
+ },
50578
+ /**
50579
+ * Runtime-state slice — mirrored by the kernel. UI players read the
50580
+ * full slice for live now-playing, volume, and progress updates
50581
+ * without polling.
50582
+ */
50583
+ runtimeState: MediaPlayerStatusSchema,
50584
+ /**
50585
+ * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
50586
+ *
50587
+ * See `RuntimeStateDurability`. Enforced by
50588
+ * `scripts/check-runtime-state-durability.ts`.
50589
+ */
50590
+ durability: "session",
50591
+ /**
50592
+ * History (D850): volume (a level an operator or an automation sets; the
50593
+ * default 10 s floor folds a slider drag into its end value) and mute.
50594
+ * `position` is not recorded: it moves continuously while playing.
50595
+ */
50596
+ history: { fields: {
50597
+ volumeLevel: {
50598
+ kind: "gauge",
50599
+ unit: "%",
50600
+ deadband: 1
50601
+ },
50602
+ isMuted: { kind: "boolean" }
50603
+ } }
50604
+ };
50352
50605
  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;
50353
50606
  var NUMBER = {
50354
50607
  kind: "fixed",
@@ -50796,117 +51049,6 @@ function actuator(input) {
50796
51049
  };
50797
51050
  }
50798
51051
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
50799
- var PowerSourceRefusalReasonSchema = _enum([
50800
- "no-power-source",
50801
- "switch-missing",
50802
- "switch-offline",
50803
- "switch-unknown",
50804
- "restart-in-progress",
50805
- "switch-call-failed"
50806
- ]);
50807
- var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
50808
- accepted: literal(false),
50809
- reason: PowerSourceRefusalReasonSchema,
50810
- /** One line for the operator, naming the switch. */
50811
- message: string()
50812
- })]);
50813
- var PowerSourceInfoSchema = object({
50814
- /** False when the device has no power source linked; the rest is then empty. */
50815
- linked: boolean(),
50816
- switchDeviceId: number().nullable(),
50817
- switchName: string().nullable(),
50818
- /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
50819
- switchOn: boolean().nullable(),
50820
- switchOnline: boolean().nullable(),
50821
- restartInProgress: boolean(),
50822
- offSeconds: number().int()
50823
- });
50824
- /** The composed slice: the switch state the link mirrors, plus the stored off time. */
50825
- var PowerSourceStateSchema = object({
50826
- /** Null while the switch has not reported: unknown is never `false` (D393). */
50827
- switchOn: boolean().nullable(),
50828
- offSeconds: number().int(),
50829
- lastFetchedAt: number()
50830
- });
50831
- var POWER_SOURCE_DEVICE_TYPES = [
50832
- DeviceType.Gateway,
50833
- DeviceType.Hub,
50834
- DeviceType.Camera,
50835
- DeviceType.Sensor,
50836
- DeviceType.MediaPlayer,
50837
- DeviceType.Vacuum,
50838
- DeviceType.Generic,
50839
- DeviceType.Washer,
50840
- DeviceType.Dryer,
50841
- DeviceType.Dishwasher,
50842
- DeviceType.PetFountain,
50843
- DeviceType.Fan,
50844
- DeviceType.Humidifier,
50845
- DeviceType.Climate,
50846
- DeviceType.WaterHeater
50847
- ];
50848
- var powerSourceCustomize = {
50849
- version: 1,
50850
- targets: [{
50851
- id: "switch",
50852
- cardinality: "scalar",
50853
- vocabulary: {
50854
- cap: "power-source",
50855
- stateField: "switchOn"
50856
- },
50857
- deviceTypes: POWER_SOURCE_DEVICE_TYPES,
50858
- apply: "add",
50859
- path: "switchOn",
50860
- sources: [{
50861
- kind: "switch",
50862
- transforms: [{ kind: "identity" }]
50863
- }],
50864
- companions: [{
50865
- kind: "constant",
50866
- path: "offSeconds",
50867
- value: 10,
50868
- when: "add"
50869
- }, {
50870
- kind: "clock",
50871
- path: "lastFetchedAt",
50872
- when: "always"
50873
- }],
50874
- sourceReport: { status: "assignment-status" }
50875
- }]
50876
- };
50877
- var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
50878
- var powerSourceCapability = {
50879
- name: "power-source",
50880
- scope: "device",
50881
- mode: "singleton",
50882
- deviceTypes: POWER_SOURCE_DEVICE_TYPES,
50883
- methods: {
50884
- getStatus: systemMethod(deviceInput$2, PowerSourceInfoSchema, { auth: "admin" }),
50885
- powerOn: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
50886
- kind: "mutation",
50887
- auth: "admin"
50888
- }),
50889
- powerOff: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
50890
- kind: "mutation",
50891
- auth: "admin"
50892
- }),
50893
- forceRestart: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
50894
- kind: "mutation",
50895
- auth: "admin"
50896
- })
50897
- },
50898
- runtimeState: PowerSourceStateSchema,
50899
- customize: powerSourceCustomize,
50900
- /**
50901
- * Runtime-state durability: **session** — mirrored from the linked switch on
50902
- * its next report; a restored value would outlive the reading it names.
50903
- *
50904
- * See `RuntimeStateDurability`. Enforced by
50905
- * `scripts/check-runtime-state-durability.ts`.
50906
- */
50907
- durability: "session",
50908
- history: "none"
50909
- };
50910
51052
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
50911
51053
  DeviceType.Camera;
50912
51054
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
@@ -50934,6 +51076,165 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
50934
51076
  }).optional()
50935
51077
  })) });
50936
51078
  /**
51079
+ * A saved Customize assignment (D936). The schema is the type: callers use
51080
+ * `z.infer`, they do not restate the shape.
51081
+ */
51082
+ var OverrideSourceSchema = object({
51083
+ addonId: string().min(1),
51084
+ stableId: string().min(1),
51085
+ cap: string().min(1),
51086
+ fieldPath: string().min(1)
51087
+ });
51088
+ var IdentityTransformDraftSchema = object({ kind: literal("identity") });
51089
+ var InvertTransformDraftSchema = object({ kind: literal("invert") });
51090
+ var ThresholdTransformDraftSchema = object({
51091
+ kind: literal("threshold"),
51092
+ comparison: _enum(["gte", "lte"]),
51093
+ threshold: number(),
51094
+ startDwellMs: number().nonnegative(),
51095
+ stopDwellMs: number().nonnegative()
51096
+ });
51097
+ var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
51098
+ IdentityTransformDraftSchema,
51099
+ InvertTransformDraftSchema,
51100
+ ThresholdTransformDraftSchema,
51101
+ object({
51102
+ kind: literal("linear"),
51103
+ scale: number(),
51104
+ offset: number(),
51105
+ clampMin: number().optional(),
51106
+ clampMax: number().optional()
51107
+ }),
51108
+ object({
51109
+ kind: literal("enum-map"),
51110
+ mapping: record(string(), string().nullable())
51111
+ }),
51112
+ object({ kind: literal("number") })
51113
+ ]);
51114
+ var ScalarOverrideDraftSchema = object({
51115
+ kind: literal("scalar"),
51116
+ cap: string().min(1),
51117
+ targetId: string().min(1),
51118
+ source: OverrideSourceSchema,
51119
+ transform: CustomizeTransformDraftSchema
51120
+ });
51121
+ var KeyedArrayOverrideDraftSchema = object({
51122
+ kind: literal("keyedArray"),
51123
+ cap: string().min(1),
51124
+ targetId: string().min(1),
51125
+ key: string().min(1),
51126
+ label: string().min(1),
51127
+ fieldPath: string().min(1),
51128
+ source: OverrideSourceSchema,
51129
+ transform: CustomizeTransformDraftSchema
51130
+ });
51131
+ var PulseOverrideDraftSchema = object({
51132
+ kind: literal("pulse"),
51133
+ cap: string().min(1),
51134
+ targetId: string().min(1),
51135
+ enabled: boolean(),
51136
+ debounceMs: number().nonnegative(),
51137
+ edges: array(_enum([
51138
+ "rising",
51139
+ "button",
51140
+ "native-event"
51141
+ ])).min(1).optional(),
51142
+ sources: array(OverrideSourceSchema).min(1)
51143
+ });
51144
+ var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
51145
+ "in-addition",
51146
+ "confirm",
51147
+ "replace"
51148
+ ]).optional() });
51149
+ discriminatedUnion("kind", [
51150
+ ScalarOverrideDraftSchema,
51151
+ KeyedArrayOverrideDraftSchema,
51152
+ PulseOverrideDraftSchema,
51153
+ object({
51154
+ kind: literal("span"),
51155
+ cap: string().min(1),
51156
+ targetId: string().min(1),
51157
+ timeoutMs: number().nonnegative(),
51158
+ cooldownMs: number().nonnegative().optional(),
51159
+ sources: array(SpanSourceDraftSchema)
51160
+ })
51161
+ ]);
51162
+ object({
51163
+ targetId: string().min(1),
51164
+ configured: boolean(),
51165
+ source: _enum(["native", "assigned"]),
51166
+ state: _enum([
51167
+ "active",
51168
+ "stale",
51169
+ "released",
51170
+ "unavailable"
51171
+ ]),
51172
+ reason: string().nullable()
51173
+ });
51174
+ /**
51175
+ * What a running-threshold proposal says (planning#405). The schema is the
51176
+ * type: the algorithm, the capability method and the UI all read `z.infer`.
51177
+ *
51178
+ * A proposal is read-only advice: it carries the numbers it was derived from
51179
+ * (`evidence`) so the operator can judge it, and an `insufficient` answer names
51180
+ * WHY with a typed reason and the catalog defaults, never an invented number.
51181
+ */
51182
+ var RunningProposalTierSchema = _enum(["raw", "5m"]);
51183
+ var RunningProposalConfidenceSchema = _enum([
51184
+ "high",
51185
+ "medium",
51186
+ "low"
51187
+ ]);
51188
+ var RunningProposalInsufficientReasonSchema = _enum([
51189
+ "no-history",
51190
+ "span-too-short",
51191
+ "truncated",
51192
+ "no-idle-floor",
51193
+ "no-runs",
51194
+ "no-separation"
51195
+ ]);
51196
+ var RunningProposalReplaySchema = object({
51197
+ /** Intervals the real dwell expression held true over the window. */
51198
+ intervals: number().int().nonnegative(),
51199
+ /** Intervals outside every run, or a second interval inside one. */
51200
+ extra: number().int().nonnegative(),
51201
+ /** Runs without an interval of their own (none, or merged into a neighbour). */
51202
+ missed: number().int().nonnegative()
51203
+ });
51204
+ var RunningProposalEvidenceSchema = object({
51205
+ tier: RunningProposalTierSchema,
51206
+ spanMs: number().nonnegative(),
51207
+ from: number(),
51208
+ to: number(),
51209
+ points: number().int().nonnegative(),
51210
+ runsFound: number().int().nonnegative(),
51211
+ pausesFound: number().int().nonnegative(),
51212
+ longestPauseMs: number().nonnegative(),
51213
+ idleFloorW: number(),
51214
+ noiseCeilingW: number(),
51215
+ plateauW: number(),
51216
+ replay: RunningProposalReplaySchema
51217
+ });
51218
+ var RunningProposalInsufficientEvidenceSchema = object({
51219
+ spanMs: number().nonnegative(),
51220
+ points: number().int().nonnegative(),
51221
+ runsFound: number().int().nonnegative()
51222
+ });
51223
+ var RunningThresholdProposalSchema = discriminatedUnion("state", [object({
51224
+ state: literal("proposal"),
51225
+ transform: ThresholdTransformDraftSchema,
51226
+ evidence: RunningProposalEvidenceSchema,
51227
+ confidence: RunningProposalConfidenceSchema,
51228
+ /** Why the confidence is not higher; empty when nothing held it back. */
51229
+ notes: array(string())
51230
+ }), object({
51231
+ state: literal("insufficient"),
51232
+ reason: RunningProposalInsufficientReasonSchema,
51233
+ evidence: RunningProposalInsufficientEvidenceSchema,
51234
+ /** The catalog defaults, offered as what stands when nothing better is known. */
51235
+ fallback: ThresholdTransformDraftSchema
51236
+ })]);
51237
+ /**
50937
51238
  * The report of a one-shot "move this owner's plaintext secrets into the
50938
51239
  * keychain" run (D878). COUNTS only - never a value, never a ref.
50939
51240
  *
@@ -51230,6 +51531,14 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
51230
51531
  kind: "mutation",
51231
51532
  auth: "admin",
51232
51533
  access: "view"
51534
+ }), method(object({
51535
+ deviceId: number().int(),
51536
+ cap: string().min(1),
51537
+ fieldPath: string().min(1),
51538
+ deviceType: _enum(DeviceType).optional()
51539
+ }), RunningThresholdProposalSchema, {
51540
+ auth: "admin",
51541
+ access: "view"
51233
51542
  }), method(object({
51234
51543
  blockId: string().min(1),
51235
51544
  trace: AutomationRunTraceSchema.optional(),
@@ -55927,15 +56236,15 @@ var FanPresetCaptureResultSchema = object({
55927
56236
  items: array(FanCaptureItemSchema),
55928
56237
  omitted: array(FanCaptureOmissionSchema)
55929
56238
  });
55930
- var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
56239
+ var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
55931
56240
  DeviceType.Fan;
55932
- method(deviceInput$1, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$1.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
56241
+ method(deviceInput$2, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$2.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
55933
56242
  kind: "mutation",
55934
56243
  auth: "admin"
55935
- }), method(deviceInput$1.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
56244
+ }), method(deviceInput$2.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
55936
56245
  kind: "mutation",
55937
56246
  auth: "protected"
55938
- }), method(deviceInput$1.extend({
56247
+ }), method(deviceInput$2.extend({
55939
56248
  fields: array(FanCaptureFieldSchema).optional(),
55940
56249
  steps: array(ActionStepSchema).max(20).default([])
55941
56250
  }), FanPresetCaptureResultSchema, { auth: "protected" });
@@ -59229,214 +59538,6 @@ var LoginMethodContributionSchema = discriminatedUnion("kind", [
59229
59538
  ]);
59230
59539
  method(_void(), array(LoginMethodContributionSchema).readonly());
59231
59540
  /**
59232
- * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
59233
- * Apple TV, Spotify, Roku, generic OTT receivers, …) on
59234
- * `DeviceType.MediaPlayer`. Disjoint from the camera streaming
59235
- * surface — those use `DeviceType.Camera` + the broker pipeline.
59236
- *
59237
- * The cap declares a wide method surface; not every backend supports
59238
- * every method. Drivers signal sub-capability via the
59239
- * `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
59240
- * corresponding method) so the UI gates buttons without a slice
59241
- * fetch — same pattern as `PanTiltZoom` for cameras.
59242
- */
59243
- var MediaPlayerStateSchema = _enum([
59244
- "off",
59245
- "on",
59246
- "idle",
59247
- "playing",
59248
- "paused",
59249
- "buffering",
59250
- "standby"
59251
- ]);
59252
- var MediaPlayerRepeatSchema = _enum([
59253
- "off",
59254
- "all",
59255
- "one"
59256
- ]);
59257
- var MediaInfoSchema = object({
59258
- /** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
59259
- * `podcast`, …). */
59260
- type: string(),
59261
- /** Human-readable title. */
59262
- title: string(),
59263
- /** Optional artist / channel / station label. */
59264
- artist: string().optional(),
59265
- /** Optional album / season / show name. */
59266
- album: string().optional(),
59267
- /** Optional cover-art / thumbnail URL. */
59268
- imageUrl: string().optional()
59269
- });
59270
- /**
59271
- * Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
59272
- * the device said so. `assumed`: the driver has no feedback (infrared) and
59273
- * tracks what it last SENT; it moves only after a send was accepted, and a
59274
- * human at the real remote makes it wrong. A UI shows an assumed value as a
59275
- * guess, never as a reading.
59276
- */
59277
- var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
59278
- /** The fields of a media-player status a driver can only guess. */
59279
- var MediaPlayerAssumedFieldSchema = _enum([
59280
- "state",
59281
- "volumeLevel",
59282
- "isMuted",
59283
- "source"
59284
- ]);
59285
- var MediaPlayerStatusSchema = object({
59286
- /** Playback lifecycle state. */
59287
- state: MediaPlayerStateSchema,
59288
- /**
59289
- * Whether the state fields are readings or the driver's own bookkeeping.
59290
- * A slice written before this field existed parses as `reported`.
59291
- */
59292
- stateSource: MediaPlayerStateSourceSchema.default("reported"),
59293
- /**
59294
- * Which fields are the driver's own guess. `stateSource` is `assumed` when
59295
- * this is not empty; a field not listed here is a reading even then (power
59296
- * from a running-state source, mute guessed from the last send).
59297
- */
59298
- assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
59299
- /** Volume as 0..100 inclusive. Null when the device has no volume
59300
- * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
59301
- volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59302
- /** Mute toggle distinct from volume=0. Null when no mute surface.
59303
- * Pair with `DeviceFeature.MediaPlayerMute`. */
59304
- isMuted: boolean().nullable(),
59305
- /** Active source / input. Empty when no source surface. */
59306
- source: string(),
59307
- /** Selectable sources. Empty when no source surface.
59308
- * Pair with `DeviceFeature.MediaPlayerSelectSource`. */
59309
- availableSources: array(string()),
59310
- /** Currently-playing media info. Null when nothing is playing. */
59311
- currentMedia: MediaInfoSchema.nullable(),
59312
- /** Current playback position in ms. Null when not seekable or
59313
- * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
59314
- position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59315
- /** Total duration of the current media in ms. Null when unknown
59316
- * (live stream) or nothing is playing. */
59317
- duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59318
- /** Shuffle toggle. Null when no shuffle surface.
59319
- * Pair with `DeviceFeature.MediaPlayerShuffle`. */
59320
- shuffle: boolean().nullable(),
59321
- /** Repeat mode. Null when no repeat surface.
59322
- * Pair with `DeviceFeature.MediaPlayerRepeat`. */
59323
- repeat: MediaPlayerRepeatSchema.nullable(),
59324
- /** Ms epoch when the slice was last updated. */
59325
- lastChangedAt: number()
59326
- });
59327
- var mediaPlayerCapability = {
59328
- name: "media-player",
59329
- scope: "device",
59330
- deviceNative: true,
59331
- mode: "singleton",
59332
- deviceTypes: [DeviceType.MediaPlayer],
59333
- methods: {
59334
- play: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59335
- kind: "mutation",
59336
- auth: "protected"
59337
- }),
59338
- pause: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59339
- kind: "mutation",
59340
- auth: "protected"
59341
- }),
59342
- stop: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59343
- kind: "mutation",
59344
- auth: "protected"
59345
- }),
59346
- next: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59347
- kind: "mutation",
59348
- auth: "protected"
59349
- }),
59350
- previous: method(object({ deviceId: number().int().nonnegative() }), _void(), {
59351
- kind: "mutation",
59352
- auth: "protected"
59353
- }),
59354
- seek: method(object({
59355
- deviceId: number().int().nonnegative(),
59356
- position: number().int().nonnegative()
59357
- }), _void(), {
59358
- kind: "mutation",
59359
- auth: "protected"
59360
- }),
59361
- setVolume: method(object({
59362
- deviceId: number().int().nonnegative(),
59363
- volumeLevel: number().min(0).max(100)
59364
- }), _void(), {
59365
- kind: "mutation",
59366
- auth: "protected"
59367
- }),
59368
- setMute: method(object({
59369
- deviceId: number().int().nonnegative(),
59370
- muted: boolean()
59371
- }), _void(), {
59372
- kind: "mutation",
59373
- auth: "protected"
59374
- }),
59375
- setShuffle: method(object({
59376
- deviceId: number().int().nonnegative(),
59377
- shuffle: boolean()
59378
- }), _void(), {
59379
- kind: "mutation",
59380
- auth: "protected"
59381
- }),
59382
- setRepeat: method(object({
59383
- deviceId: number().int().nonnegative(),
59384
- repeat: MediaPlayerRepeatSchema
59385
- }), _void(), {
59386
- kind: "mutation",
59387
- auth: "protected"
59388
- }),
59389
- selectSource: method(object({
59390
- deviceId: number().int().nonnegative(),
59391
- source: string().min(1)
59392
- }), _void(), {
59393
- kind: "mutation",
59394
- auth: "protected"
59395
- }),
59396
- playMedia: method(object({
59397
- deviceId: number().int().nonnegative(),
59398
- /** Media identifier / URL. */
59399
- mediaId: string().min(1),
59400
- /** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
59401
- * passes it through to the upstream service. */
59402
- mediaType: string().min(1)
59403
- }), _void(), {
59404
- kind: "mutation",
59405
- auth: "protected"
59406
- })
59407
- },
59408
- status: {
59409
- schema: MediaPlayerStatusSchema,
59410
- kind: "push"
59411
- },
59412
- /**
59413
- * Runtime-state slice — mirrored by the kernel. UI players read the
59414
- * full slice for live now-playing, volume, and progress updates
59415
- * without polling.
59416
- */
59417
- runtimeState: MediaPlayerStatusSchema,
59418
- /**
59419
- * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
59420
- *
59421
- * See `RuntimeStateDurability`. Enforced by
59422
- * `scripts/check-runtime-state-durability.ts`.
59423
- */
59424
- durability: "session",
59425
- /**
59426
- * History (D850): volume (a level an operator or an automation sets; the
59427
- * default 10 s floor folds a slider drag into its end value) and mute.
59428
- * `position` is not recorded: it moves continuously while playing.
59429
- */
59430
- history: { fields: {
59431
- volumeLevel: {
59432
- kind: "gauge",
59433
- unit: "%",
59434
- deadband: 1
59435
- },
59436
- isMuted: { kind: "boolean" }
59437
- } }
59438
- };
59439
- /**
59440
59541
  * mesh-network — collection cap for mesh-VPN providers.
59441
59542
  *
59442
59543
  * Different shape from `network-access` (public tunnels):
@@ -73520,6 +73621,14 @@ var powerMeterCapability = {
73520
73621
  deviceNative: true,
73521
73622
  mode: "singleton",
73522
73623
  deviceTypes: POWER_METER_DEVICE_TYPES,
73624
+ deviceConfig: { ui: {
73625
+ kind: "widget",
73626
+ widgetId: "host/power-meter-line",
73627
+ tab: "power",
73628
+ placement: "hero",
73629
+ label: "Power",
73630
+ order: 4
73631
+ } },
73523
73632
  methods: {},
73524
73633
  status: {
73525
73634
  schema: PowerMeterStatusSchema,
@@ -73579,6 +73688,117 @@ var powerMeterCapability = {
73579
73688
  volatileStateFields: ["lastFetchedAt"],
73580
73689
  customize: powerMeterCustomize
73581
73690
  };
73691
+ var PowerSourceRefusalReasonSchema = _enum([
73692
+ "no-power-source",
73693
+ "switch-missing",
73694
+ "switch-offline",
73695
+ "switch-unknown",
73696
+ "restart-in-progress",
73697
+ "switch-call-failed"
73698
+ ]);
73699
+ var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
73700
+ accepted: literal(false),
73701
+ reason: PowerSourceRefusalReasonSchema,
73702
+ /** One line for the operator, naming the switch. */
73703
+ message: string()
73704
+ })]);
73705
+ var PowerSourceInfoSchema = object({
73706
+ /** False when the device has no power source linked; the rest is then empty. */
73707
+ linked: boolean(),
73708
+ switchDeviceId: number().nullable(),
73709
+ switchName: string().nullable(),
73710
+ /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
73711
+ switchOn: boolean().nullable(),
73712
+ switchOnline: boolean().nullable(),
73713
+ restartInProgress: boolean(),
73714
+ offSeconds: number().int()
73715
+ });
73716
+ /** The composed slice: the switch state the link mirrors, plus the stored off time. */
73717
+ var PowerSourceStateSchema = object({
73718
+ /** Null while the switch has not reported: unknown is never `false` (D393). */
73719
+ switchOn: boolean().nullable(),
73720
+ offSeconds: number().int(),
73721
+ lastFetchedAt: number()
73722
+ });
73723
+ var POWER_SOURCE_DEVICE_TYPES = [
73724
+ DeviceType.Gateway,
73725
+ DeviceType.Hub,
73726
+ DeviceType.Camera,
73727
+ DeviceType.Sensor,
73728
+ DeviceType.MediaPlayer,
73729
+ DeviceType.Vacuum,
73730
+ DeviceType.Generic,
73731
+ DeviceType.Washer,
73732
+ DeviceType.Dryer,
73733
+ DeviceType.Dishwasher,
73734
+ DeviceType.PetFountain,
73735
+ DeviceType.Fan,
73736
+ DeviceType.Humidifier,
73737
+ DeviceType.Climate,
73738
+ DeviceType.WaterHeater
73739
+ ];
73740
+ var powerSourceCustomize = {
73741
+ version: 1,
73742
+ targets: [{
73743
+ id: "switch",
73744
+ cardinality: "scalar",
73745
+ vocabulary: {
73746
+ cap: "power-source",
73747
+ stateField: "switchOn"
73748
+ },
73749
+ deviceTypes: POWER_SOURCE_DEVICE_TYPES,
73750
+ apply: "add",
73751
+ path: "switchOn",
73752
+ sources: [{
73753
+ kind: "switch",
73754
+ transforms: [{ kind: "identity" }]
73755
+ }],
73756
+ companions: [{
73757
+ kind: "constant",
73758
+ path: "offSeconds",
73759
+ value: 10,
73760
+ when: "add"
73761
+ }, {
73762
+ kind: "clock",
73763
+ path: "lastFetchedAt",
73764
+ when: "always"
73765
+ }],
73766
+ sourceReport: { status: "assignment-status" }
73767
+ }]
73768
+ };
73769
+ var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
73770
+ var powerSourceCapability = {
73771
+ name: "power-source",
73772
+ scope: "device",
73773
+ mode: "singleton",
73774
+ deviceTypes: POWER_SOURCE_DEVICE_TYPES,
73775
+ methods: {
73776
+ getStatus: systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "admin" }),
73777
+ powerOn: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73778
+ kind: "mutation",
73779
+ auth: "admin"
73780
+ }),
73781
+ powerOff: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73782
+ kind: "mutation",
73783
+ auth: "admin"
73784
+ }),
73785
+ forceRestart: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
73786
+ kind: "mutation",
73787
+ auth: "admin"
73788
+ })
73789
+ },
73790
+ runtimeState: PowerSourceStateSchema,
73791
+ customize: powerSourceCustomize,
73792
+ /**
73793
+ * Runtime-state durability: **session** — mirrored from the linked switch on
73794
+ * its next report; a restored value would outlive the reading it names.
73795
+ *
73796
+ * See `RuntimeStateDurability`. Enforced by
73797
+ * `scripts/check-runtime-state-durability.ts`.
73798
+ */
73799
+ durability: "session",
73800
+ history: "none"
73801
+ };
73582
73802
  /**
73583
73803
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
73584
73804
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -77113,7 +77333,13 @@ var RemoteMediaCommandSchema = _enum([
77113
77333
  "next",
77114
77334
  "previous",
77115
77335
  "setMute",
77116
- "selectSource"
77336
+ "selectSource",
77337
+ "turnOn",
77338
+ "turnOff",
77339
+ "volumeUp",
77340
+ "volumeDown",
77341
+ "channelUp",
77342
+ "channelDown"
77117
77343
  ]);
77118
77344
  /** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
77119
77345
  var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
@@ -77134,7 +77360,7 @@ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.e
77134
77360
  }), RemoteSendResultSchema, {
77135
77361
  kind: "mutation",
77136
77362
  auth: "protected"
77137
- }), method(deviceInput.extend({
77363
+ }), method(deviceInput, MediaPlayerStatusSchema.nullable(), { auth: "protected" }), method(deviceInput.extend({
77138
77364
  command: RemoteMediaCommandSchema,
77139
77365
  muted: boolean().optional(),
77140
77366
  source: string().optional(),
@@ -79418,7 +79644,11 @@ listSignals: method(_void(), array(StreamSignalSchema).readonly()) }
79418
79644
  * `getSettingsUISchema()` surface.
79419
79645
  */
79420
79646
  var SwitchStatusSchema = object({
79421
- on: boolean().meta({ label: "On" }),
79647
+ /**
79648
+ * `null` = the device has never reported its state (a measurement that
79649
+ * has not happened is unknown, never `false`).
79650
+ */
79651
+ on: boolean().nullable().meta({ label: "On" }),
79422
79652
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
79423
79653
  lastChangedAt: number()
79424
79654
  });
@@ -79443,7 +79673,7 @@ var switchCapability = {
79443
79673
  }) },
79444
79674
  events: { onStateChanged: { data: object({
79445
79675
  deviceId: number(),
79446
- on: boolean(),
79676
+ on: boolean().nullable(),
79447
79677
  lastChangedAt: number()
79448
79678
  }) } },
79449
79679
  status: {
@@ -83046,6 +83276,7 @@ function buildEventKindDescriptor(kind, source) {
83046
83276
  }
83047
83277
  };
83048
83278
  }
83279
+ DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
83049
83280
  var btn = (idSuffix, row, col, init) => ({
83050
83281
  idSuffix,
83051
83282
  row,
@@ -83172,6 +83403,34 @@ discriminatedUnion("kind", [object({
83172
83403
  kind: literal("pronto"),
83173
83404
  code: string()
83174
83405
  })]);
83406
+ /**
83407
+ * Propose a running threshold from a device's own power history (planning#405).
83408
+ *
83409
+ * Pure and deterministic: the same series gives the same answer. The series is
83410
+ * a HELD-VALUE step series — a point's value stands until the next point — which
83411
+ * is how device-history stores a gauge, so "time at a level" is exact, not a
83412
+ * count of samples.
83413
+ *
83414
+ * 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
83415
+ * near F. A device that is not near one level most of the time has no floor.
83416
+ * 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
83417
+ * 3. gaps between episodes split in two by the largest log-scale jump (>= 3x):
83418
+ * the short ones are pauses INSIDE a run (a soak), the long ones separate runs.
83419
+ * 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
83420
+ * grid a person would type; stop dwell covers the longest pause; start dwell
83421
+ * covers the longest standby blip above T.
83422
+ * 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
83423
+ * run must hold exactly one true interval and nothing may flap outside.
83424
+ */
83425
+ var SECOND_MS = 1e3;
83426
+ var MINUTE_MS$1 = 60 * SECOND_MS;
83427
+ 24 * (60 * MINUTE_MS$1);
83428
+ 10 * MINUTE_MS$1;
83429
+ 30 * SECOND_MS;
83430
+ 60 * SECOND_MS;
83431
+ 20 * MINUTE_MS$1;
83432
+ 10 * SECOND_MS;
83433
+ 10 * SECOND_MS;
83175
83434
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
83176
83435
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
83177
83436
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
@@ -85836,6 +86095,13 @@ Object.freeze({
85836
86095
  access: "create",
85837
86096
  role: "admin"
85838
86097
  },
86098
+ "coreBlocks.proposeRunningThreshold": {
86099
+ capName: "core-blocks",
86100
+ capScope: "system",
86101
+ addonId: null,
86102
+ access: "view",
86103
+ role: "admin"
86104
+ },
85839
86105
  "coreBlocks.reportAutomationRun": {
85840
86106
  capName: "core-blocks",
85841
86107
  capScope: "system",
@@ -88783,6 +89049,20 @@ Object.freeze({
88783
89049
  access: "view",
88784
89050
  role: "admin"
88785
89051
  },
89052
+ "mediaPlayer.channelDown": {
89053
+ capName: "media-player",
89054
+ capScope: "device",
89055
+ addonId: null,
89056
+ access: "create",
89057
+ role: "viewer"
89058
+ },
89059
+ "mediaPlayer.channelUp": {
89060
+ capName: "media-player",
89061
+ capScope: "device",
89062
+ addonId: null,
89063
+ access: "create",
89064
+ role: "viewer"
89065
+ },
88786
89066
  "mediaPlayer.getStatus": {
88787
89067
  capName: "media-player",
88788
89068
  capScope: "device",
@@ -88874,6 +89154,34 @@ Object.freeze({
88874
89154
  access: "create",
88875
89155
  role: "viewer"
88876
89156
  },
89157
+ "mediaPlayer.turnOff": {
89158
+ capName: "media-player",
89159
+ capScope: "device",
89160
+ addonId: null,
89161
+ access: "create",
89162
+ role: "viewer"
89163
+ },
89164
+ "mediaPlayer.turnOn": {
89165
+ capName: "media-player",
89166
+ capScope: "device",
89167
+ addonId: null,
89168
+ access: "create",
89169
+ role: "viewer"
89170
+ },
89171
+ "mediaPlayer.volumeDown": {
89172
+ capName: "media-player",
89173
+ capScope: "device",
89174
+ addonId: null,
89175
+ access: "create",
89176
+ role: "viewer"
89177
+ },
89178
+ "mediaPlayer.volumeUp": {
89179
+ capName: "media-player",
89180
+ capScope: "device",
89181
+ addonId: null,
89182
+ access: "create",
89183
+ role: "viewer"
89184
+ },
88877
89185
  "meshNetwork.getStatus": {
88878
89186
  capName: "mesh-network",
88879
89187
  capScope: "system",
@@ -92430,6 +92738,13 @@ Object.freeze({
92430
92738
  access: "create",
92431
92739
  role: "admin"
92432
92740
  },
92741
+ "remote.getAssumedStatus": {
92742
+ capName: "remote",
92743
+ capScope: "system",
92744
+ addonId: null,
92745
+ access: "view",
92746
+ role: "admin"
92747
+ },
92433
92748
  "remote.getConfig": {
92434
92749
  capName: "remote",
92435
92750
  capScope: "system",
@@ -95245,6 +95560,11 @@ Object.freeze({
95245
95560
  form: "single",
95246
95561
  optional: false
95247
95562
  }],
95563
+ "coreBlocks.proposeRunningThreshold": [{
95564
+ name: "deviceId",
95565
+ form: "single",
95566
+ optional: false
95567
+ }],
95248
95568
  "cover.close": [{
95249
95569
  name: "deviceId",
95250
95570
  form: "single",
@@ -96096,6 +96416,16 @@ Object.freeze({
96096
96416
  form: "single",
96097
96417
  optional: true
96098
96418
  }],
96419
+ "mediaPlayer.channelDown": [{
96420
+ name: "deviceId",
96421
+ form: "single",
96422
+ optional: false
96423
+ }],
96424
+ "mediaPlayer.channelUp": [{
96425
+ name: "deviceId",
96426
+ form: "single",
96427
+ optional: false
96428
+ }],
96099
96429
  "mediaPlayer.getStatus": [{
96100
96430
  name: "deviceId",
96101
96431
  form: "single",
@@ -96161,6 +96491,26 @@ Object.freeze({
96161
96491
  form: "single",
96162
96492
  optional: false
96163
96493
  }],
96494
+ "mediaPlayer.turnOff": [{
96495
+ name: "deviceId",
96496
+ form: "single",
96497
+ optional: false
96498
+ }],
96499
+ "mediaPlayer.turnOn": [{
96500
+ name: "deviceId",
96501
+ form: "single",
96502
+ optional: false
96503
+ }],
96504
+ "mediaPlayer.volumeDown": [{
96505
+ name: "deviceId",
96506
+ form: "single",
96507
+ optional: false
96508
+ }],
96509
+ "mediaPlayer.volumeUp": [{
96510
+ name: "deviceId",
96511
+ form: "single",
96512
+ optional: false
96513
+ }],
96164
96514
  "meter.getStatus": [{
96165
96515
  name: "deviceId",
96166
96516
  form: "single",
@@ -97326,6 +97676,11 @@ Object.freeze({
97326
97676
  form: "single",
97327
97677
  optional: true
97328
97678
  }],
97679
+ "remote.getAssumedStatus": [{
97680
+ name: "deviceId",
97681
+ form: "single",
97682
+ optional: false
97683
+ }],
97329
97684
  "remote.getConfig": [{
97330
97685
  name: "deviceId",
97331
97686
  form: "single",
@@ -97970,99 +98325,6 @@ var BucketAccumulator = class {
97970
98325
  return n;
97971
98326
  }
97972
98327
  };
97973
- /**
97974
- * A saved Customize assignment (D936). The schema is the type: callers use
97975
- * `z.infer`, they do not restate the shape.
97976
- */
97977
- var OverrideSourceSchema = object({
97978
- addonId: string().min(1),
97979
- stableId: string().min(1),
97980
- cap: string().min(1),
97981
- fieldPath: string().min(1)
97982
- });
97983
- var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
97984
- object({ kind: literal("identity") }),
97985
- object({ kind: literal("invert") }),
97986
- object({
97987
- kind: literal("threshold"),
97988
- comparison: _enum(["gte", "lte"]),
97989
- threshold: number(),
97990
- startDwellMs: number().nonnegative(),
97991
- stopDwellMs: number().nonnegative()
97992
- }),
97993
- object({
97994
- kind: literal("linear"),
97995
- scale: number(),
97996
- offset: number(),
97997
- clampMin: number().optional(),
97998
- clampMax: number().optional()
97999
- }),
98000
- object({
98001
- kind: literal("enum-map"),
98002
- mapping: record(string(), string().nullable())
98003
- }),
98004
- object({ kind: literal("number") })
98005
- ]);
98006
- var ScalarOverrideDraftSchema = object({
98007
- kind: literal("scalar"),
98008
- cap: string().min(1),
98009
- targetId: string().min(1),
98010
- source: OverrideSourceSchema,
98011
- transform: CustomizeTransformDraftSchema
98012
- });
98013
- var KeyedArrayOverrideDraftSchema = object({
98014
- kind: literal("keyedArray"),
98015
- cap: string().min(1),
98016
- targetId: string().min(1),
98017
- key: string().min(1),
98018
- label: string().min(1),
98019
- fieldPath: string().min(1),
98020
- source: OverrideSourceSchema,
98021
- transform: CustomizeTransformDraftSchema
98022
- });
98023
- var PulseOverrideDraftSchema = object({
98024
- kind: literal("pulse"),
98025
- cap: string().min(1),
98026
- targetId: string().min(1),
98027
- enabled: boolean(),
98028
- debounceMs: number().nonnegative(),
98029
- edges: array(_enum([
98030
- "rising",
98031
- "button",
98032
- "native-event"
98033
- ])).min(1).optional(),
98034
- sources: array(OverrideSourceSchema).min(1)
98035
- });
98036
- var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
98037
- "in-addition",
98038
- "confirm",
98039
- "replace"
98040
- ]).optional() });
98041
- discriminatedUnion("kind", [
98042
- ScalarOverrideDraftSchema,
98043
- KeyedArrayOverrideDraftSchema,
98044
- PulseOverrideDraftSchema,
98045
- object({
98046
- kind: literal("span"),
98047
- cap: string().min(1),
98048
- targetId: string().min(1),
98049
- timeoutMs: number().nonnegative(),
98050
- cooldownMs: number().nonnegative().optional(),
98051
- sources: array(SpanSourceDraftSchema)
98052
- })
98053
- ]);
98054
- object({
98055
- targetId: string().min(1),
98056
- configured: boolean(),
98057
- source: _enum(["native", "assigned"]),
98058
- state: _enum([
98059
- "active",
98060
- "stale",
98061
- "released",
98062
- "unavailable"
98063
- ]),
98064
- reason: string().nullable()
98065
- });
98066
98328
  var G711_SCALE_CORRECTION_DB = {
98067
98329
  PCMU: 20 * Math.log10(4),
98068
98330
  PCMA: 20 * Math.log10(8)
@@ -100394,6 +100656,16 @@ var NC_TEMPLATE_VARS = [
100394
100656
  kinds: ["system"],
100395
100657
  systemEventKinds: [...COMBINED_ALARM]
100396
100658
  },
100659
+ {
100660
+ name: "changes",
100661
+ label: "Intrusion changes",
100662
+ example: "Laundry lock unlocked, Back door open",
100663
+ group: "system",
100664
+ families: ["rule"],
100665
+ fields: [...RULE_TEXT],
100666
+ kinds: ["system"],
100667
+ systemEventKinds: [...COMBINED_ALARM]
100668
+ },
100397
100669
  {
100398
100670
  name: "deviceId",
100399
100671
  label: "Device id",