@camstack/addon-provider-velux 0.2.154 → 0.2.155

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.
package/dist/addon.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { connect } from "node:tls";
3
3
  import { setImmediate as setImmediate$1 } from "timers/promises";
4
- import { connect as connect$1 } from "node:net";
5
4
  //#region \0rolldown/runtime.js
6
5
  var __create = Object.create;
7
6
  var __defProp = Object.defineProperty;
@@ -35,7 +34,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
35
34
  }) : target, mod));
36
35
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
37
36
  //#endregion
38
- //#region ../types/dist/event-category-CaURIwQA.mjs
37
+ //#region ../types/dist/event-category-BmPm98n0.mjs
39
38
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
40
39
  EventCategory["SystemBoot"] = "system.boot";
41
40
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -519,6 +518,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
519
518
  EventCategory["RunningStarted"] = "running.started";
520
519
  /** Mirrors `runningStateCapability.events.onFinished`. */
521
520
  EventCategory["RunningFinished"] = "running.finished";
521
+ /** Mirrors `deviceRunsCapability.events.onRunOpened` - a durable run record opened. */
522
+ EventCategory["DeviceRunOpened"] = "device-run.opened";
523
+ /** Mirrors `deviceRunsCapability.events.onRunClosed` - a durable run record closed. */
524
+ EventCategory["DeviceRunClosed"] = "device-run.closed";
522
525
  /**
523
526
  * Cap event fired by every device that registers the `event-emitter`
524
527
  * capability. Mirrors `eventEmitterCapability.events.onEvent`. Carries
@@ -5867,7 +5870,7 @@ var ZodIssueCode = {
5867
5870
  var ZodFirstPartyTypeKind;
5868
5871
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5869
5872
  //#endregion
5870
- //#region ../types/dist/sleep-CSYuxssh.mjs
5873
+ //#region ../types/dist/sleep-BKzhQTSm.mjs
5871
5874
  /**
5872
5875
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5873
5876
  * window to float samples (D455).
@@ -9380,6 +9383,7 @@ var CAP_NAMES = Object.freeze([
9380
9383
  "device-ops",
9381
9384
  "device-provider",
9382
9385
  "device-removal-hook",
9386
+ "device-runs",
9383
9387
  "device-state",
9384
9388
  "device-status",
9385
9389
  "dishwasher",
@@ -11324,7 +11328,7 @@ object({
11324
11328
  params: record(string(), union([string(), number()])).optional()
11325
11329
  });
11326
11330
  //#endregion
11327
- //#region ../types/dist/composition-BBzVf2xd.mjs
11331
+ //#region ../types/dist/composition-BiKWlUCc.mjs
11328
11332
  /**
11329
11333
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11330
11334
  *
@@ -12257,7 +12261,15 @@ var HelperSpecSchema = discriminatedUnion("kind", [
12257
12261
  thresholdWatts: number().positive(),
12258
12262
  startDwellMs: number().int().nonnegative(),
12259
12263
  stopDwellMs: number().int().nonnegative(),
12260
- member: HelperMemberSchema
12264
+ member: HelperMemberSchema,
12265
+ /**
12266
+ * The plug that powers the appliance (`switch.on`). Optional: when set, the
12267
+ * composition carries a `power-source` feature, so the hub can cut and restore
12268
+ * that plug (power-source link of a NEW-target composition).
12269
+ */
12270
+ powerSwitch: HelperMemberSchema.optional(),
12271
+ /** How long a forced restart keeps the plug off; the cap's default when unset. */
12272
+ offSeconds: number().optional()
12261
12273
  }),
12262
12274
  object({
12263
12275
  kind: literal("statistic"),
@@ -13655,7 +13667,13 @@ var MigrationLedgerEntrySchema = object({
13655
13667
  /** Why it is `blocked` or `held` (`dependency-failed`, `snapshot-refused`, …). */
13656
13668
  reason: string().nullable(),
13657
13669
  nodeId: string().nullable(),
13658
- bootId: string().nullable()
13670
+ bootId: string().nullable(),
13671
+ /**
13672
+ * The catalogued meta of this id (description, body kind, store, `dependsOn`…),
13673
+ * joined by the engine from the catalogues it knows this boot. Absent when no
13674
+ * catalogue lists the id (a newer database).
13675
+ */
13676
+ meta: MigrationMetaSchema.optional()
13659
13677
  });
13660
13678
  /** A catalogued migration that is not applied, with where it stands. */
13661
13679
  var MigrationPendingEntrySchema = object({
@@ -19462,6 +19480,14 @@ var APPLIANCE_CAP_VOCABULARY = {
19462
19480
  }
19463
19481
  } }
19464
19482
  },
19483
+ "device-runs": {
19484
+ scope: "device",
19485
+ noun: t("Runs", "Esecuzioni"),
19486
+ events: {
19487
+ onRunOpened: { notOperatorFacing: "the durable record of a run that the running-state start flip already announces" },
19488
+ onRunClosed: { notOperatorFacing: "the durable record of a run that the running-state finish flip already announces" }
19489
+ }
19490
+ },
19465
19491
  update: {
19466
19492
  scope: "device",
19467
19493
  noun: t("Firmware update", "Aggiornamento firmware"),
@@ -46044,6 +46070,117 @@ function actuator(input) {
46044
46070
  };
46045
46071
  }
46046
46072
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
46073
+ var PowerSourceRefusalReasonSchema = _enum([
46074
+ "no-power-source",
46075
+ "switch-missing",
46076
+ "switch-offline",
46077
+ "switch-unknown",
46078
+ "restart-in-progress",
46079
+ "switch-call-failed"
46080
+ ]);
46081
+ var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
46082
+ accepted: literal(false),
46083
+ reason: PowerSourceRefusalReasonSchema,
46084
+ /** One line for the operator, naming the switch. */
46085
+ message: string()
46086
+ })]);
46087
+ var PowerSourceInfoSchema = object({
46088
+ /** False when the device has no power source linked; the rest is then empty. */
46089
+ linked: boolean(),
46090
+ switchDeviceId: number().nullable(),
46091
+ switchName: string().nullable(),
46092
+ /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
46093
+ switchOn: boolean().nullable(),
46094
+ switchOnline: boolean().nullable(),
46095
+ restartInProgress: boolean(),
46096
+ offSeconds: number().int()
46097
+ });
46098
+ /** The composed slice: the switch state the link mirrors, plus the stored off time. */
46099
+ var PowerSourceStateSchema = object({
46100
+ /** Null while the switch has not reported: unknown is never `false` (D393). */
46101
+ switchOn: boolean().nullable(),
46102
+ offSeconds: number().int(),
46103
+ lastFetchedAt: number()
46104
+ });
46105
+ var POWER_SOURCE_DEVICE_TYPES = [
46106
+ DeviceType.Gateway,
46107
+ DeviceType.Hub,
46108
+ DeviceType.Camera,
46109
+ DeviceType.Sensor,
46110
+ DeviceType.MediaPlayer,
46111
+ DeviceType.Vacuum,
46112
+ DeviceType.Generic,
46113
+ DeviceType.Washer,
46114
+ DeviceType.Dryer,
46115
+ DeviceType.Dishwasher,
46116
+ DeviceType.PetFountain,
46117
+ DeviceType.Fan,
46118
+ DeviceType.Humidifier,
46119
+ DeviceType.Climate,
46120
+ DeviceType.WaterHeater
46121
+ ];
46122
+ var powerSourceCustomize = {
46123
+ version: 1,
46124
+ targets: [{
46125
+ id: "switch",
46126
+ cardinality: "scalar",
46127
+ vocabulary: {
46128
+ cap: "power-source",
46129
+ stateField: "switchOn"
46130
+ },
46131
+ deviceTypes: POWER_SOURCE_DEVICE_TYPES,
46132
+ apply: "add",
46133
+ path: "switchOn",
46134
+ sources: [{
46135
+ kind: "switch",
46136
+ transforms: [{ kind: "identity" }]
46137
+ }],
46138
+ companions: [{
46139
+ kind: "constant",
46140
+ path: "offSeconds",
46141
+ value: 10,
46142
+ when: "add"
46143
+ }, {
46144
+ kind: "clock",
46145
+ path: "lastFetchedAt",
46146
+ when: "always"
46147
+ }],
46148
+ sourceReport: { status: "assignment-status" }
46149
+ }]
46150
+ };
46151
+ var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
46152
+ var powerSourceCapability = {
46153
+ name: "power-source",
46154
+ scope: "device",
46155
+ mode: "singleton",
46156
+ deviceTypes: POWER_SOURCE_DEVICE_TYPES,
46157
+ methods: {
46158
+ getStatus: systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "admin" }),
46159
+ powerOn: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
46160
+ kind: "mutation",
46161
+ auth: "admin"
46162
+ }),
46163
+ powerOff: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
46164
+ kind: "mutation",
46165
+ auth: "admin"
46166
+ }),
46167
+ forceRestart: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
46168
+ kind: "mutation",
46169
+ auth: "admin"
46170
+ })
46171
+ },
46172
+ runtimeState: PowerSourceStateSchema,
46173
+ customize: powerSourceCustomize,
46174
+ /**
46175
+ * Runtime-state durability: **session** — mirrored from the linked switch on
46176
+ * its next report; a restored value would outlive the reading it names.
46177
+ *
46178
+ * See `RuntimeStateDurability`. Enforced by
46179
+ * `scripts/check-runtime-state-durability.ts`.
46180
+ */
46181
+ durability: "session",
46182
+ history: "none"
46183
+ };
46047
46184
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
46048
46185
  DeviceType.Camera;
46049
46186
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
@@ -47469,6 +47606,101 @@ method(object({ deviceId: number().int() }), array(SeriesInfoSchema).readonly())
47469
47606
  kind: "mutation",
47470
47607
  auth: "admin"
47471
47608
  });
47609
+ /**
47610
+ * device-runs - the durable record of ONE run of a device: a washing cycle, a
47611
+ * dishwasher cycle, later a mow or a clean (D992).
47612
+ *
47613
+ * A run is a DOCUMENT (opened at one instant, closed at another, carrying what
47614
+ * was measured over it), never a sample - device-history keeps the numbers over
47615
+ * time and its `device-runs` provider keeps these documents beside them, because
47616
+ * the energy of a run is a question about device-history's own series. The
47617
+ * record is written from the `running-state` flip on `DeviceStateChanged`; it
47618
+ * survives a restart (the open row is on disk) and a missed flip (boot compares
47619
+ * the open rows against the live snapshots).
47620
+ *
47621
+ * Every measurement is `number | null` and `null` means "could not be
47622
+ * measured", never `0` (D393). A run closed by the boot reconcile because the
47623
+ * device stopped while nobody was watching has `outcome: 'unknown'`.
47624
+ */
47625
+ var DeviceRunKindSchema = _enum([
47626
+ "appliance",
47627
+ "mower",
47628
+ "vacuum"
47629
+ ]);
47630
+ var DeviceRunStateSchema = _enum(["open", "closed"]);
47631
+ var DeviceRunOutcomeSchema = _enum([
47632
+ "completed",
47633
+ "aborted",
47634
+ "error",
47635
+ "unknown"
47636
+ ]);
47637
+ /** What opened or closed the run. `reconcile` is the boot comparison against live state. */
47638
+ var DeviceRunSourceSchema = _enum([
47639
+ "running-state",
47640
+ "activity",
47641
+ "reconcile"
47642
+ ]);
47643
+ /** How the energy of a run was obtained; `null` when it could not be. */
47644
+ var DeviceRunEnergyMethodSchema = _enum(["counter-delta", "history-integral"]);
47645
+ var DeviceRunPhaseSchema = object({
47646
+ phase: string(),
47647
+ at: number()
47648
+ });
47649
+ /** The kind-specific block; a later phase adds a branch, nothing else changes. */
47650
+ var DeviceRunDetailSchema = discriminatedUnion("kind", [object({
47651
+ kind: literal("appliance"),
47652
+ /** kWh used over the run. */
47653
+ energyKwh: number().nullable().meta(readingMeta("energy")),
47654
+ energyMethod: DeviceRunEnergyMethodSchema.nullable(),
47655
+ /** Highest power seen while the run was open. */
47656
+ peakW: number().nullable().meta(readingMeta("power")),
47657
+ /** Time-weighted mean power while the run was open. */
47658
+ avgW: number().nullable().meta(readingMeta("power")),
47659
+ /** The program the appliance said it ran when the run opened. */
47660
+ program: string().nullable(),
47661
+ remainingAtStartSec: number().nullable().meta(readingMeta("duration")),
47662
+ /** The last phase the device reported before the run closed. */
47663
+ finalPhase: string().nullable(),
47664
+ /**
47665
+ * The meter's energy counter when the run opened; `null` when the meter had
47666
+ * not said (or the run was opened late, by the boot reconcile). It lives in
47667
+ * the row so a run that outlives a restart still ends with `end - start`.
47668
+ */
47669
+ energyStartKwh: number().nullable().meta(readingMeta("energy")),
47670
+ /** Milliseconds of power readings behind `avgW` - the weight a restart resumes the mean with. */
47671
+ powerObservedMs: number()
47672
+ })]);
47673
+ var DeviceRunSchema = object({
47674
+ /** `${deviceId}:${startedAt}`. */
47675
+ runId: string(),
47676
+ deviceId: number().int(),
47677
+ kind: DeviceRunKindSchema,
47678
+ state: DeviceRunStateSchema,
47679
+ startedAt: number(),
47680
+ endedAt: number().nullable(),
47681
+ durationMs: number().nullable(),
47682
+ outcome: DeviceRunOutcomeSchema.nullable(),
47683
+ startSource: DeviceRunSourceSchema,
47684
+ endSource: DeviceRunSourceSchema.nullable(),
47685
+ /** Where the device's running bit came from (`running-state.source`); `null` when it did not say. */
47686
+ runningSource: _enum(["native", "assigned"]).nullable(),
47687
+ phases: array(DeviceRunPhaseSchema),
47688
+ detail: DeviceRunDetailSchema
47689
+ });
47690
+ DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, method(object({
47691
+ deviceId: number().int(),
47692
+ outcome: DeviceRunOutcomeSchema.optional(),
47693
+ atFrom: number().optional(),
47694
+ atTo: number().optional(),
47695
+ limit: number().int().positive().max(200).default(50),
47696
+ cursor: string().optional()
47697
+ }), object({
47698
+ runs: array(DeviceRunSchema).readonly(),
47699
+ nextCursor: string().nullable()
47700
+ })), method(object({
47701
+ deviceId: number().int(),
47702
+ runId: string()
47703
+ }), DeviceRunSchema.nullable()), method(object({ deviceId: number().int() }), DeviceRunSchema.nullable());
47472
47704
  var DeviceCaStatusSchema = discriminatedUnion("state", [
47473
47705
  object({ state: literal("absent") }),
47474
47706
  object({
@@ -47604,8 +47836,8 @@ method(object({}), object({
47604
47836
  });
47605
47837
  var KeychainKeySourceSchema = object({
47606
47838
  kind: _enum([
47607
- "config-file",
47608
- "previous-config-file",
47839
+ "mounted-file",
47840
+ "previous-mounted-file",
47609
47841
  "platform",
47610
47842
  "default-file"
47611
47843
  ]),
@@ -52101,7 +52333,8 @@ var PALETTE_ENTRY_IDS = [
52101
52333
  "switch",
52102
52334
  "siren",
52103
52335
  "button",
52104
- "doorbell"
52336
+ "doorbell",
52337
+ "appliance-cycle"
52105
52338
  ];
52106
52339
  var AUDIO_ENTRY_ID = "audio";
52107
52340
  /** Six rungs, quiet → loud. The opaque end is the entry's hex. */
@@ -52172,7 +52405,8 @@ var CAMSTACK_HEX = {
52172
52405
  switch: "#7a812a",
52173
52406
  siren: "#ac5d7f",
52174
52407
  button: "#b276cd",
52175
- doorbell: "#0087ab"
52408
+ doorbell: "#0087ab",
52409
+ "appliance-cycle": "#2a8c8c"
52176
52410
  };
52177
52411
  var CAMSTACK_FALLBACK = [
52178
52412
  "#b34dc7",
@@ -52237,7 +52471,8 @@ var HIGH_CONTRAST_HEX = {
52237
52471
  switch: "#d05259",
52238
52472
  siren: "#cd67b3",
52239
52473
  button: "#c9746a",
52240
- doorbell: "#338eeb"
52474
+ doorbell: "#338eeb",
52475
+ "appliance-cycle": "#00848a"
52241
52476
  };
52242
52477
  var HIGH_CONTRAST_FALLBACK = [
52243
52478
  "#817f3b",
@@ -52302,7 +52537,8 @@ var OKABE_ITO_HEX = {
52302
52537
  switch: "#ad2c4e",
52303
52538
  siren: "#348e08",
52304
52539
  button: "#d27303",
52305
- doorbell: "#55770f"
52540
+ doorbell: "#55770f",
52541
+ "appliance-cycle": "#00737d"
52306
52542
  };
52307
52543
  var OKABE_ITO_FALLBACK = [
52308
52544
  "#9a3784",
@@ -52511,6 +52747,7 @@ sub("switch", "control", "control", "switch", "Switch");
52511
52747
  sub("siren", "control", "control", "siren", "Siren");
52512
52748
  sub("button", "control", "control", "button", "Button");
52513
52749
  sub("doorbell", "control", "control", "doorbell", "Doorbell");
52750
+ sub("appliance-cycle", "control", "control", "switch", "Appliance cycle");
52514
52751
  for (const l of AUDIO_MACRO_LABELS) {
52515
52752
  const kind = `audio-${l.id}`;
52516
52753
  if (entries.has(kind)) continue;
@@ -61142,7 +61379,8 @@ var CAP_TO_KIND = {
61142
61379
  cover: "cover",
61143
61380
  switch: "switch",
61144
61381
  button: "button",
61145
- doorbell: "doorbell"
61382
+ doorbell: "doorbell",
61383
+ "running-state": "appliance-cycle"
61146
61384
  };
61147
61385
  function buildDescriptor(capName, kind) {
61148
61386
  const t = EVENT_TAXONOMY[kind];
@@ -64272,10 +64510,7 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
64272
64510
  }), InstallPackageResultSchema, {
64273
64511
  kind: "mutation",
64274
64512
  auth: "admin"
64275
- }), method(object({ packageName: string() }), InstallPackageResultSchema, {
64276
- kind: "mutation",
64277
- auth: "admin"
64278
- }), method(_void(), boolean()), method(_void(), array(unknown()).readonly()), method(object({
64513
+ }), method(object({
64279
64514
  packageName: string().min(1),
64280
64515
  nodeId: string().optional(),
64281
64516
  everywhere: boolean().optional()
@@ -65536,19 +65771,19 @@ var RemoteSetConfigResultSchema = discriminatedUnion("status", [object({ status:
65536
65771
  reason: _enum(["unknown-template", "not-a-media-player"]),
65537
65772
  detail: string()
65538
65773
  })]);
65539
- var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
65774
+ var deviceInput = object({ deviceId: number().int().nonnegative() });
65540
65775
  DeviceType.MediaPlayer;
65541
- method(deviceInput$1, RemoteConfigSchema, { auth: "admin" }), method(deviceInput$1.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
65776
+ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
65542
65777
  kind: "mutation",
65543
65778
  auth: "admin"
65544
- }), method(deviceInput$1, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput$1.extend({
65779
+ }), method(deviceInput, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput.extend({
65545
65780
  role: RemoteRoleSchema,
65546
65781
  /** Sends in a row (volume steps). */
65547
65782
  repeat: number().int().min(1).max(50).default(1)
65548
65783
  }), RemoteSendResultSchema, {
65549
65784
  kind: "mutation",
65550
65785
  auth: "protected"
65551
- }), method(deviceInput$1.extend({
65786
+ }), method(deviceInput.extend({
65552
65787
  command: RemoteMediaCommandSchema,
65553
65788
  muted: boolean().optional(),
65554
65789
  source: string().optional(),
@@ -69922,117 +70157,6 @@ var lockControlCapability = {
69922
70157
  * whether persisting is worth a SQLite commit. */
69923
70158
  volatileStateFields: ["lastChangedAt"]
69924
70159
  };
69925
- var PowerSourceRefusalReasonSchema = _enum([
69926
- "no-power-source",
69927
- "switch-missing",
69928
- "switch-offline",
69929
- "switch-unknown",
69930
- "restart-in-progress",
69931
- "switch-call-failed"
69932
- ]);
69933
- var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
69934
- accepted: literal(false),
69935
- reason: PowerSourceRefusalReasonSchema,
69936
- /** One line for the operator, naming the switch. */
69937
- message: string()
69938
- })]);
69939
- var PowerSourceInfoSchema = object({
69940
- /** False when the device has no power source linked; the rest is then empty. */
69941
- linked: boolean(),
69942
- switchDeviceId: number().nullable(),
69943
- switchName: string().nullable(),
69944
- /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
69945
- switchOn: boolean().nullable(),
69946
- switchOnline: boolean().nullable(),
69947
- restartInProgress: boolean(),
69948
- offSeconds: number().int()
69949
- });
69950
- /** The composed slice: the switch state the link mirrors, plus the stored off time. */
69951
- var PowerSourceStateSchema = object({
69952
- /** Null while the switch has not reported: unknown is never `false` (D393). */
69953
- switchOn: boolean().nullable(),
69954
- offSeconds: number().int(),
69955
- lastFetchedAt: number()
69956
- });
69957
- var POWER_SOURCE_DEVICE_TYPES = [
69958
- DeviceType.Gateway,
69959
- DeviceType.Hub,
69960
- DeviceType.Camera,
69961
- DeviceType.Sensor,
69962
- DeviceType.MediaPlayer,
69963
- DeviceType.Vacuum,
69964
- DeviceType.Generic,
69965
- DeviceType.Washer,
69966
- DeviceType.Dryer,
69967
- DeviceType.Dishwasher,
69968
- DeviceType.PetFountain,
69969
- DeviceType.Fan,
69970
- DeviceType.Humidifier,
69971
- DeviceType.Climate,
69972
- DeviceType.WaterHeater
69973
- ];
69974
- var powerSourceCustomize = {
69975
- version: 1,
69976
- targets: [{
69977
- id: "switch",
69978
- cardinality: "scalar",
69979
- vocabulary: {
69980
- cap: "power-source",
69981
- stateField: "switchOn"
69982
- },
69983
- deviceTypes: POWER_SOURCE_DEVICE_TYPES,
69984
- apply: "add",
69985
- path: "switchOn",
69986
- sources: [{
69987
- kind: "switch",
69988
- transforms: [{ kind: "identity" }]
69989
- }],
69990
- companions: [{
69991
- kind: "constant",
69992
- path: "offSeconds",
69993
- value: 10,
69994
- when: "add"
69995
- }, {
69996
- kind: "clock",
69997
- path: "lastFetchedAt",
69998
- when: "always"
69999
- }],
70000
- sourceReport: { status: "assignment-status" }
70001
- }]
70002
- };
70003
- var deviceInput = object({ deviceId: number().int().nonnegative() });
70004
- var powerSourceCapability = {
70005
- name: "power-source",
70006
- scope: "device",
70007
- mode: "singleton",
70008
- deviceTypes: POWER_SOURCE_DEVICE_TYPES,
70009
- methods: {
70010
- getStatus: systemMethod(deviceInput, PowerSourceInfoSchema, { auth: "admin" }),
70011
- powerOn: systemMethod(deviceInput, PowerSourceActionResultSchema, {
70012
- kind: "mutation",
70013
- auth: "admin"
70014
- }),
70015
- powerOff: systemMethod(deviceInput, PowerSourceActionResultSchema, {
70016
- kind: "mutation",
70017
- auth: "admin"
70018
- }),
70019
- forceRestart: systemMethod(deviceInput, PowerSourceActionResultSchema, {
70020
- kind: "mutation",
70021
- auth: "admin"
70022
- })
70023
- },
70024
- runtimeState: PowerSourceStateSchema,
70025
- customize: powerSourceCustomize,
70026
- /**
70027
- * Runtime-state durability: **session** — mirrored from the linked switch on
70028
- * its next report; a restored value would outlive the reading it names.
70029
- *
70030
- * See `RuntimeStateDurability`. Enforced by
70031
- * `scripts/check-runtime-state-durability.ts`.
70032
- */
70033
- durability: "session",
70034
- history: "none"
70035
- };
70036
70160
  /**
70037
70161
  * A monotonic total between resets (D944). Energy uses `power-meter.energy`
70038
70162
  * so dashboards keep one series; every other instantaneous reading lands here.
@@ -73574,6 +73698,10 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
73574
73698
  /** Generic Customize target for the running boolean (D936). */
73575
73699
  var runningCustomize = {
73576
73700
  version: 1,
73701
+ overridable: [{
73702
+ path: "running",
73703
+ by: "running"
73704
+ }],
73577
73705
  targets: [{
73578
73706
  id: "running",
73579
73707
  cardinality: "scalar",
@@ -73631,7 +73759,7 @@ var runningCustomize = {
73631
73759
  kind: "constant",
73632
73760
  path: "phase",
73633
73761
  value: null,
73634
- when: "always"
73762
+ when: "add"
73635
73763
  },
73636
73764
  {
73637
73765
  kind: "clock",
@@ -76941,7 +77069,7 @@ var ExternalIdentityKeySchema = object({
76941
77069
  provider: string().min(1),
76942
77070
  subject: string().min(1)
76943
77071
  });
76944
- method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserInputSchema, UserSummarySchema, {
77072
+ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(_void(), object({ defaultPasswordInUse: boolean() }), { auth: "admin" }), method(CreateUserInputSchema, UserSummarySchema, {
76945
77073
  kind: "mutation",
76946
77074
  auth: "admin",
76947
77075
  access: "create"
@@ -80280,13 +80408,6 @@ Object.freeze({
80280
80408
  access: "view",
80281
80409
  role: "admin"
80282
80410
  },
80283
- "addons.installFromWorkspace": {
80284
- capName: "addons",
80285
- capScope: "system",
80286
- addonId: null,
80287
- access: "create",
80288
- role: "admin"
80289
- },
80290
80411
  "addons.installPackage": {
80291
80412
  capName: "addons",
80292
80413
  capScope: "system",
@@ -80294,13 +80415,6 @@ Object.freeze({
80294
80415
  access: "create",
80295
80416
  role: "admin"
80296
80417
  },
80297
- "addons.isWorkspaceAvailable": {
80298
- capName: "addons",
80299
- capScope: "system",
80300
- addonId: null,
80301
- access: "view",
80302
- role: "admin"
80303
- },
80304
80418
  "addons.list": {
80305
80419
  capName: "addons",
80306
80420
  capScope: "system",
@@ -80350,13 +80464,6 @@ Object.freeze({
80350
80464
  access: "view",
80351
80465
  role: "admin"
80352
80466
  },
80353
- "addons.listWorkspacePackages": {
80354
- capName: "addons",
80355
- capScope: "system",
80356
- addonId: null,
80357
- access: "view",
80358
- role: "admin"
80359
- },
80360
80467
  "addons.onAddonLogs": {
80361
80468
  capName: "addons",
80362
80469
  capScope: "system",
@@ -83024,6 +83131,27 @@ Object.freeze({
83024
83131
  access: "create",
83025
83132
  role: "admin"
83026
83133
  },
83134
+ "deviceRuns.getCurrentRun": {
83135
+ capName: "device-runs",
83136
+ capScope: "device",
83137
+ addonId: null,
83138
+ access: "view",
83139
+ role: "viewer"
83140
+ },
83141
+ "deviceRuns.getRun": {
83142
+ capName: "device-runs",
83143
+ capScope: "device",
83144
+ addonId: null,
83145
+ access: "view",
83146
+ role: "viewer"
83147
+ },
83148
+ "deviceRuns.listRuns": {
83149
+ capName: "device-runs",
83150
+ capScope: "device",
83151
+ addonId: null,
83152
+ access: "view",
83153
+ role: "viewer"
83154
+ },
83027
83155
  "deviceState.claimFields": {
83028
83156
  capName: "device-state",
83029
83157
  capScope: "system",
@@ -89737,6 +89865,13 @@ Object.freeze({
89737
89865
  access: "view",
89738
89866
  role: "admin"
89739
89867
  },
89868
+ "userManagement.getAdminBootstrapStatus": {
89869
+ capName: "user-management",
89870
+ capScope: "system",
89871
+ addonId: null,
89872
+ access: "view",
89873
+ role: "admin"
89874
+ },
89740
89875
  "userManagement.getTotpStatus": {
89741
89876
  capName: "user-management",
89742
89877
  capScope: "system",
@@ -91222,6 +91357,21 @@ Object.freeze({
91222
91357
  form: "single",
91223
91358
  optional: false
91224
91359
  }],
91360
+ "deviceRuns.getCurrentRun": [{
91361
+ name: "deviceId",
91362
+ form: "single",
91363
+ optional: false
91364
+ }],
91365
+ "deviceRuns.getRun": [{
91366
+ name: "deviceId",
91367
+ form: "single",
91368
+ optional: false
91369
+ }],
91370
+ "deviceRuns.listRuns": [{
91371
+ name: "deviceId",
91372
+ form: "single",
91373
+ optional: false
91374
+ }],
91225
91375
  "deviceState.claimFields": [{
91226
91376
  name: "deviceId",
91227
91377
  form: "single",
@@ -93704,13 +93854,114 @@ NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
93704
93854
  *
93705
93855
  * Pure, RN-safe: the viewer imports the support rule from here.
93706
93856
  */
93857
+ /**
93858
+ * Control caps that still have a raise and a clear an operator names: an
93859
+ * appliance cycle raises when it STARTS and clears when it FINISHES. Kept out
93860
+ * of `SOURCE_CAP_ACTIVE_FIELD` on purpose — that table also makes a device a
93861
+ * camera-sensor / recorder-trigger source, and a dishwasher is neither.
93862
+ */
93863
+ var CYCLE_RAISED_FIELD_BY_CAP = { "running-state": "running" };
93707
93864
  /** Event kind → the boolean field of its cap slice that means "raised". */
93708
93865
  var NC_SENSOR_RAISED_FIELD_BY_KIND = Object.freeze(Object.fromEntries(Object.entries(EVENT_KIND_BY_CAP).flatMap(([capName, descriptor]) => {
93866
+ const cycleField = CYCLE_RAISED_FIELD_BY_CAP[capName];
93867
+ if (cycleField !== void 0) return [[descriptor.kind, cycleField]];
93709
93868
  const field = SOURCE_CAP_ACTIVE_FIELD[capName];
93710
93869
  if (field === void 0 || descriptor.category !== "sensor") return [];
93711
93870
  return [[descriptor.kind, field]];
93712
93871
  })));
93713
93872
  Object.freeze(Object.keys(NC_SENSOR_RAISED_FIELD_BY_KIND));
93873
+ Object.freeze({
93874
+ contact: {
93875
+ starts: {
93876
+ en: "opened",
93877
+ it: "si apre"
93878
+ },
93879
+ finishes: {
93880
+ en: "closed",
93881
+ it: "si chiude"
93882
+ }
93883
+ },
93884
+ "motion-sensor": {
93885
+ starts: {
93886
+ en: "motion detected",
93887
+ it: "movimento rilevato"
93888
+ },
93889
+ finishes: {
93890
+ en: "motion stopped",
93891
+ it: "movimento terminato"
93892
+ }
93893
+ },
93894
+ smoke: {
93895
+ starts: {
93896
+ en: "smoke detected",
93897
+ it: "fumo rilevato"
93898
+ },
93899
+ finishes: {
93900
+ en: "smoke gone",
93901
+ it: "fumo scomparso"
93902
+ }
93903
+ },
93904
+ flood: {
93905
+ starts: {
93906
+ en: "water detected",
93907
+ it: "acqua rilevata"
93908
+ },
93909
+ finishes: {
93910
+ en: "dry again",
93911
+ it: "di nuovo asciutto"
93912
+ }
93913
+ },
93914
+ gas: {
93915
+ starts: {
93916
+ en: "gas detected",
93917
+ it: "gas rilevato"
93918
+ },
93919
+ finishes: {
93920
+ en: "gas gone",
93921
+ it: "gas scomparso"
93922
+ }
93923
+ },
93924
+ "carbon-monoxide": {
93925
+ starts: {
93926
+ en: "carbon monoxide detected",
93927
+ it: "monossido rilevato"
93928
+ },
93929
+ finishes: {
93930
+ en: "carbon monoxide gone",
93931
+ it: "monossido scomparso"
93932
+ }
93933
+ },
93934
+ vibration: {
93935
+ starts: {
93936
+ en: "vibration detected",
93937
+ it: "vibrazione rilevata"
93938
+ },
93939
+ finishes: {
93940
+ en: "vibration stopped",
93941
+ it: "vibrazione terminata"
93942
+ }
93943
+ },
93944
+ tamper: {
93945
+ starts: {
93946
+ en: "tampered with",
93947
+ it: "manomesso"
93948
+ },
93949
+ finishes: {
93950
+ en: "tamper cleared",
93951
+ it: "manomissione rientrata"
93952
+ }
93953
+ },
93954
+ "appliance-cycle": {
93955
+ starts: {
93956
+ en: "the cycle started",
93957
+ it: "il ciclo parte"
93958
+ },
93959
+ finishes: {
93960
+ en: "the cycle finished",
93961
+ it: "il ciclo è terminato"
93962
+ }
93963
+ }
93964
+ });
93714
93965
  object({
93715
93966
  phase: _enum(["raised", "resolved"]),
93716
93967
  episodeId: string().min(1),
@@ -93917,6 +94168,10 @@ var STATE_SUMMARY_VARS = [
93917
94168
  [...RULE_TEXT],
93918
94169
  [...RULE_TEXT],
93919
94170
  [...RULE_TEXT],
94171
+ [...RULE_TEXT],
94172
+ [...RULE_TEXT],
94173
+ [...RULE_TEXT],
94174
+ [...RULE_TEXT],
93920
94175
  [...RULE_BODY_ONLY],
93921
94176
  [...RULE_TEXT],
93922
94177
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
@@ -93934,6 +94189,7 @@ var STATE_SUMMARY_VARS = [
93934
94189
  [...RULE_TEXT],
93935
94190
  [...RULE_TEXT],
93936
94191
  [...RULE_TEXT],
94192
+ [...RULE_TEXT],
93937
94193
  ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
93938
94194
  [...RULE_TEXT],
93939
94195
  [...RULE_TEXT],
@@ -94178,6 +94434,7 @@ new Set([
94178
94434
  "siren",
94179
94435
  "button",
94180
94436
  "doorbell",
94437
+ "appliance-cycle",
94181
94438
  "audio-speech",
94182
94439
  "audio-scream",
94183
94440
  "audio-crying",
@@ -103487,22 +103744,26 @@ var VeluxClientRegistry = class {
103487
103744
  };
103488
103745
  //#endregion
103489
103746
  //#region src/klf-probe.ts
103490
- /** The gateway's TLS API port. */
103491
- var KLF_PORT = 51200;
103492
103747
  /**
103493
- * Defaults add up, worst case, to 3 + 2 x 5 + 2 + 2 = 17 s, under the
103494
- * `CONNECTION_TEST_TIMEOUT_MS` ceiling of the wizard, so a stuck gateway is named
103495
- * here instead of the wizard giving up unnamed.
103748
+ * Defaults add up, worst case, to 12 + 3 + 1.5 + 1.5 = 18 s, which is the
103749
+ * wizard's `CONNECTION_TEST_TIMEOUT_MS` ceiling (20 s) minus the 2 s margin of
103750
+ * `DEFAULT_TEST_DEADLINE_MS`, so a stuck gateway is named here instead of the
103751
+ * wizard giving up unnamed. The handshake gets 12 s because a real KLF-200 needs
103752
+ * about 4.2 s for ONE handshake (TLSv1.2, DHE-RSA-AES256-SHA256 on a weak CPU).
103753
+ *
103754
+ * A probe opens exactly ONE connection: the gateway frees a closed slot very
103755
+ * slowly (measured 2026-11-01: a bare TCP connect and close before the TLS connect
103756
+ * left the real session without a handshake for 20 s), so no pre-check connects first.
103496
103757
  */
103497
103758
  var DEFAULT_KLF_PROBE_TIMEOUTS_MS = {
103498
- tcp: 3e3,
103499
- login: 5e3,
103500
- version: 2e3,
103501
- logout: 2e3
103759
+ tls: 12e3,
103760
+ password: 3e3,
103761
+ version: 1500,
103762
+ logout: 1500
103502
103763
  };
103503
103764
  var PHASE_TEXT = {
103504
- tcp: "did not accept a TCP connection on port 51200",
103505
- login: "did not complete the secure login (TLS handshake or password answer)",
103765
+ tls: "did not accept a connection or complete the TLS handshake on port 51200",
103766
+ password: "did not answer the password",
103506
103767
  version: "did not answer the version request",
103507
103768
  logout: "did not close the session"
103508
103769
  };
@@ -103529,26 +103790,43 @@ async function withDeadline(work, ms, onTimeout) {
103529
103790
  clearTimeout(timer);
103530
103791
  }
103531
103792
  }
103532
- /** A bare TCP connect to the gateway port: tells "nothing listens" from "TLS is stuck". */
103533
- function tcpReachable(host, ms) {
103534
- return new Promise((resolve, reject) => {
103535
- const socket = connect$1({
103536
- host,
103537
- port: KLF_PORT
103538
- });
103539
- socket.setTimeout(ms, () => {
103540
- socket.destroy();
103541
- reject(new KlfProbeTimeoutError("tcp", host, ms));
103542
- });
103543
- socket.once("connect", () => {
103544
- socket.destroy();
103545
- resolve();
103546
- });
103547
- socket.once("error", (err) => {
103548
- socket.destroy();
103549
- reject(err);
103550
- });
103793
+ /** How often the login watcher checks whether the handshake has finished. */
103794
+ var PHASE_POLL_MS = 20;
103795
+ /**
103796
+ * The library's `loginAsync` runs the TLS handshake and the password answer as one
103797
+ * call. `KLF200SocketProtocol` appears when the handshake is done and the password
103798
+ * is on its way, so watching it splits the call into two bounded, named steps.
103799
+ */
103800
+ async function loginInPhases(connection, host, password, limits) {
103801
+ let handshakeDone = false;
103802
+ let passwordTimer;
103803
+ let tlsTimer;
103804
+ let poll;
103805
+ const tlsError = () => new KlfProbeTimeoutError("tls", host, limits.tls);
103806
+ const passwordError = () => new KlfProbeTimeoutError("password", host, limits.password);
103807
+ const bounds = new Promise((_resolve, reject) => {
103808
+ tlsTimer = setTimeout(() => reject(tlsError()), limits.tls);
103809
+ poll = setInterval(() => {
103810
+ if (handshakeDone || connection.KLF200SocketProtocol === void 0) return;
103811
+ handshakeDone = true;
103812
+ clearTimeout(tlsTimer);
103813
+ clearInterval(poll);
103814
+ passwordTimer = setTimeout(() => reject(passwordError()), limits.password);
103815
+ }, PHASE_POLL_MS);
103551
103816
  });
103817
+ const libraryS = (limits.tls + limits.password) / 1e3;
103818
+ try {
103819
+ await Promise.race([connection.loginAsync(password, libraryS), bounds]);
103820
+ } catch (err) {
103821
+ if (err instanceof KlfProbeTimeoutError) throw err;
103822
+ if (/^timeout$/i.test(errMsg(err)) || err instanceof Error && err.name === "TimeoutError") throw handshakeDone ? passwordError() : tlsError();
103823
+ throw err;
103824
+ } finally {
103825
+ clearTimeout(tlsTimer);
103826
+ clearTimeout(passwordTimer);
103827
+ clearInterval(poll);
103828
+ bounds.catch(() => void 0);
103829
+ }
103552
103830
  }
103553
103831
  /**
103554
103832
  * Opens ONE session, authenticates, reads the firmware version and closes the
@@ -103561,15 +103839,13 @@ async function probeKlfGateway(options) {
103561
103839
  ...DEFAULT_KLF_PROBE_TIMEOUTS_MS,
103562
103840
  ...options.timeoutsMs
103563
103841
  };
103564
- await tcpReachable(options.host, limits.tcp);
103565
103842
  const connection = options.tls === void 0 ? new Connection(options.host) : new Connection(options.host, options.tls);
103566
- const loginError = () => new KlfProbeTimeoutError("login", options.host, limits.login);
103567
103843
  try {
103568
103844
  try {
103569
- await withDeadline(connection.loginAsync(options.password, limits.login / 1e3), limits.login * 2 + 500, loginError);
103845
+ await loginInPhases(connection, options.host, options.password, limits);
103570
103846
  } catch (err) {
103571
103847
  if (err instanceof KlfProbeTimeoutError) throw err;
103572
- if (/^timeout$/i.test(errMsg(err)) || err instanceof Error && err.name === "TimeoutError") throw loginError();
103848
+ if (err instanceof Error && typeof Reflect.get(err, "code") === "string") throw err;
103573
103849
  throw loginFailure(err);
103574
103850
  }
103575
103851
  try {