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