@enyo-energy/energy-app-sdk 0.0.187 → 0.0.188
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/cjs/implementations/onboarding-v2/define-onboarding-guide-v2.cjs +45 -0
- package/dist/cjs/implementations/onboarding-v2/define-onboarding-guide-v2.d.cts +36 -1
- package/dist/cjs/implementations/onboarding-v2/onboarding-v2-validators.cjs +115 -3
- package/dist/cjs/types/enyo-air-conditioning-appliance.cjs +6 -0
- package/dist/cjs/types/enyo-air-conditioning-appliance.d.cts +7 -1
- package/dist/cjs/types/enyo-heating-rod-appliance.cjs +10 -0
- package/dist/cjs/types/enyo-heating-rod-appliance.d.cts +11 -1
- package/dist/cjs/types/enyo-onboarding-v2.cjs +17 -2
- package/dist/cjs/types/enyo-onboarding-v2.d.cts +142 -6
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/implementations/onboarding-v2/define-onboarding-guide-v2.d.ts +36 -1
- package/dist/implementations/onboarding-v2/define-onboarding-guide-v2.js +45 -0
- package/dist/implementations/onboarding-v2/onboarding-v2-validators.js +116 -4
- package/dist/types/enyo-air-conditioning-appliance.d.ts +7 -1
- package/dist/types/enyo-air-conditioning-appliance.js +6 -0
- package/dist/types/enyo-heating-rod-appliance.d.ts +11 -1
- package/dist/types/enyo-heating-rod-appliance.js +10 -0
- package/dist/types/enyo-onboarding-v2.d.ts +142 -6
- package/dist/types/enyo-onboarding-v2.js +16 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -133,6 +133,51 @@ exports.onboardingV2Block = {
|
|
|
133
133
|
outcomes,
|
|
134
134
|
deviceSelection,
|
|
135
135
|
}),
|
|
136
|
+
/**
|
|
137
|
+
* A link block: a fixed URL the installer opens or copies.
|
|
138
|
+
*
|
|
139
|
+
* Passive content — it produces no routing handle, so a step whose only
|
|
140
|
+
* non-content block is a link still routes through `continue`.
|
|
141
|
+
*
|
|
142
|
+
* @param id - Stable block id, unique within the guide.
|
|
143
|
+
* @param url - Absolute `http(s)` URL. Other schemes are rejected by the validator.
|
|
144
|
+
* @param label - Translated link text (de/en).
|
|
145
|
+
* @param opts - Optional translated `description` and `copyable` (defaults to `true`).
|
|
146
|
+
*/
|
|
147
|
+
link: (id, url, label, opts) => ({
|
|
148
|
+
id,
|
|
149
|
+
type: enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Link,
|
|
150
|
+
url,
|
|
151
|
+
label,
|
|
152
|
+
description: opts?.description,
|
|
153
|
+
copyable: opts?.copyable ?? true,
|
|
154
|
+
}),
|
|
155
|
+
/**
|
|
156
|
+
* An input block: the installer types a value and the host checks it,
|
|
157
|
+
* producing the branch.
|
|
158
|
+
*
|
|
159
|
+
* For {@link EnyoOnboardingV2InputValueType.IpAddress} the host runs this
|
|
160
|
+
* app's registered device-test handler against the typed address; see
|
|
161
|
+
* {@link EnyoOnboardingV2InputBlock} for how a verdict picks an outcome.
|
|
162
|
+
* Route the outcomes with {@link onOutcomeV2} — there is no separate helper.
|
|
163
|
+
*
|
|
164
|
+
* @param id - Stable block id, unique within the guide.
|
|
165
|
+
* @param valueType - What is asked for (`Text` | `IpAddress` | `Number`).
|
|
166
|
+
* @param label - Translated field label (de/en).
|
|
167
|
+
* @param submitLabel - Translated submit button text (de/en).
|
|
168
|
+
* @param outcomes - The possible verdicts; each is a routing handle.
|
|
169
|
+
* @param opts - Optional translated `placeholder` and `help`.
|
|
170
|
+
*/
|
|
171
|
+
input: (id, valueType, label, submitLabel, outcomes, opts) => ({
|
|
172
|
+
id,
|
|
173
|
+
type: enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Input,
|
|
174
|
+
valueType,
|
|
175
|
+
label,
|
|
176
|
+
submitLabel,
|
|
177
|
+
outcomes,
|
|
178
|
+
placeholder: opts?.placeholder,
|
|
179
|
+
help: opts?.help,
|
|
180
|
+
}),
|
|
136
181
|
};
|
|
137
182
|
// ---------------------------------------------------------------------------
|
|
138
183
|
// Target factories
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { EnyoOnboardingTranslatedContent } from '../../types/enyo-onboarding.cjs';
|
|
13
13
|
import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2ChoiceLayout, EnyoOnboardingV2DeviceSelection } from '../../types/enyo-onboarding-v2.cjs';
|
|
14
|
-
import type { EnyoOnboardingV2ActionOutcome, EnyoOnboardingV2Block, EnyoOnboardingV2ChoiceOption, EnyoOnboardingV2DynamicKind, EnyoOnboardingV2Guide, EnyoOnboardingV2HintVariant, EnyoOnboardingV2PauseReason, EnyoOnboardingV2StartVariant, EnyoOnboardingV2Target, EnyoOnboardingV2Transition } from '../../types/enyo-onboarding-v2.cjs';
|
|
14
|
+
import type { EnyoOnboardingV2ActionOutcome, EnyoOnboardingV2Block, EnyoOnboardingV2ChoiceOption, EnyoOnboardingV2DynamicKind, EnyoOnboardingV2Guide, EnyoOnboardingV2HintVariant, EnyoOnboardingV2InputOutcome, EnyoOnboardingV2InputValueType, EnyoOnboardingV2PauseReason, EnyoOnboardingV2StartVariant, EnyoOnboardingV2Target, EnyoOnboardingV2Transition } from '../../types/enyo-onboarding-v2.cjs';
|
|
15
15
|
/**
|
|
16
16
|
* Identity helper that type-checks a v2 guide literal at definition time
|
|
17
17
|
* (mirrors `defineEnergyAppPackage`). Prefer this over a bare object literal so
|
|
@@ -98,6 +98,41 @@ export declare const onboardingV2Block: {
|
|
|
98
98
|
* @param deviceSelection - Which devices to test (defaults to `Detected`).
|
|
99
99
|
*/
|
|
100
100
|
deviceTest: (id: string, label: EnyoOnboardingTranslatedContent[], outcomes: EnyoOnboardingV2ActionOutcome[], deviceSelection?: EnyoOnboardingV2DeviceSelection) => EnyoOnboardingV2Block;
|
|
101
|
+
/**
|
|
102
|
+
* A link block: a fixed URL the installer opens or copies.
|
|
103
|
+
*
|
|
104
|
+
* Passive content — it produces no routing handle, so a step whose only
|
|
105
|
+
* non-content block is a link still routes through `continue`.
|
|
106
|
+
*
|
|
107
|
+
* @param id - Stable block id, unique within the guide.
|
|
108
|
+
* @param url - Absolute `http(s)` URL. Other schemes are rejected by the validator.
|
|
109
|
+
* @param label - Translated link text (de/en).
|
|
110
|
+
* @param opts - Optional translated `description` and `copyable` (defaults to `true`).
|
|
111
|
+
*/
|
|
112
|
+
link: (id: string, url: string, label: EnyoOnboardingTranslatedContent[], opts?: {
|
|
113
|
+
description?: EnyoOnboardingTranslatedContent[];
|
|
114
|
+
copyable?: boolean;
|
|
115
|
+
}) => EnyoOnboardingV2Block;
|
|
116
|
+
/**
|
|
117
|
+
* An input block: the installer types a value and the host checks it,
|
|
118
|
+
* producing the branch.
|
|
119
|
+
*
|
|
120
|
+
* For {@link EnyoOnboardingV2InputValueType.IpAddress} the host runs this
|
|
121
|
+
* app's registered device-test handler against the typed address; see
|
|
122
|
+
* {@link EnyoOnboardingV2InputBlock} for how a verdict picks an outcome.
|
|
123
|
+
* Route the outcomes with {@link onOutcomeV2} — there is no separate helper.
|
|
124
|
+
*
|
|
125
|
+
* @param id - Stable block id, unique within the guide.
|
|
126
|
+
* @param valueType - What is asked for (`Text` | `IpAddress` | `Number`).
|
|
127
|
+
* @param label - Translated field label (de/en).
|
|
128
|
+
* @param submitLabel - Translated submit button text (de/en).
|
|
129
|
+
* @param outcomes - The possible verdicts; each is a routing handle.
|
|
130
|
+
* @param opts - Optional translated `placeholder` and `help`.
|
|
131
|
+
*/
|
|
132
|
+
input: (id: string, valueType: EnyoOnboardingV2InputValueType, label: EnyoOnboardingTranslatedContent[], submitLabel: EnyoOnboardingTranslatedContent[], outcomes: EnyoOnboardingV2InputOutcome[], opts?: {
|
|
133
|
+
placeholder?: EnyoOnboardingTranslatedContent[];
|
|
134
|
+
help?: EnyoOnboardingTranslatedContent[];
|
|
135
|
+
}) => EnyoOnboardingV2Block;
|
|
101
136
|
};
|
|
102
137
|
/** Typed factories for each transition {@link EnyoOnboardingV2Target}. */
|
|
103
138
|
export declare const onboardingV2Target: {
|
|
@@ -70,6 +70,8 @@ function validateOnboardingGuideV2(guide) {
|
|
|
70
70
|
if (!step.blocks?.length)
|
|
71
71
|
warnings.push(`${at}: no content blocks.`);
|
|
72
72
|
validateActionBlocks(step, at, errors, warnings);
|
|
73
|
+
validateLinkBlocks(step, at, errors, warnings);
|
|
74
|
+
validateInputBlocks(step, at, errors, warnings);
|
|
73
75
|
}
|
|
74
76
|
if (!guide.startStepId || !stepIds.has(guide.startStepId)) {
|
|
75
77
|
errors.push('`startStepId` must reference an existing step.');
|
|
@@ -175,10 +177,119 @@ function validateActionBlocks(step, at, errors, warnings) {
|
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
179
|
}
|
|
180
|
+
/** An absolute `http(s)` URL — the only scheme a link block may carry. */
|
|
181
|
+
const HTTP_URL_RE = /^https?:\/\/\S+$/i;
|
|
182
|
+
/**
|
|
183
|
+
* Validates the link blocks of a step.
|
|
184
|
+
*
|
|
185
|
+
* The scheme check is **security-relevant, not cosmetic**: the installer app
|
|
186
|
+
* renders a link block as a tap target, so this keeps `javascript:`, `data:` and
|
|
187
|
+
* `file:` payloads out of it. connect-core enforces the identical rule
|
|
188
|
+
* server-side and the app re-checks before opening — three gates, deliberately.
|
|
189
|
+
* Do not relax it to a generic "looks like a URL" test.
|
|
190
|
+
*
|
|
191
|
+
* @param step - The step whose blocks are checked.
|
|
192
|
+
* @param at - Human-readable location prefix for messages.
|
|
193
|
+
* @param errors - Collector for blocking problems.
|
|
194
|
+
* @param warnings - Collector for advisory problems.
|
|
195
|
+
*/
|
|
196
|
+
function validateLinkBlocks(step, at, errors, warnings) {
|
|
197
|
+
for (const block of step.blocks ?? []) {
|
|
198
|
+
if (block.type !== enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Link)
|
|
199
|
+
continue;
|
|
200
|
+
if (!block.url?.trim()) {
|
|
201
|
+
errors.push(`${at}: link block "${block.id}" has no url.`);
|
|
202
|
+
}
|
|
203
|
+
else if (!HTTP_URL_RE.test(block.url.trim())) {
|
|
204
|
+
errors.push(`${at}: link block "${block.id}" url must be an absolute http(s) URL.`);
|
|
205
|
+
}
|
|
206
|
+
if (!block.label?.length) {
|
|
207
|
+
warnings.push(`${at}: link block "${block.id}" has no label — the raw URL is shown instead.`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** Every valid {@link EnyoOnboardingV2InputValueType} value. */
|
|
212
|
+
const INPUT_VALUE_TYPES = new Set(Object.values(enyo_onboarding_v2_js_1.EnyoOnboardingV2InputValueType));
|
|
213
|
+
/** Outcome values the host treats as "the check succeeded". */
|
|
214
|
+
const POSITIVE_INPUT_OUTCOMES = new Set(['reachable', 'success', 'found']);
|
|
215
|
+
/**
|
|
216
|
+
* The {@link EnyoDeviceTestOutcomeEnum} verdicts that collapse onto a positive
|
|
217
|
+
* input outcome; every other verdict collapses onto a negative one.
|
|
218
|
+
*/
|
|
219
|
+
const POSITIVE_DEVICE_TEST_OUTCOMES = new Set([
|
|
220
|
+
enyo_device_test_js_1.EnyoDeviceTestOutcomeEnum.AppliancesCreated,
|
|
221
|
+
enyo_device_test_js_1.EnyoDeviceTestOutcomeEnum.AppliancesAlreadyExisted,
|
|
222
|
+
enyo_device_test_js_1.EnyoDeviceTestOutcomeEnum.DeviceConfirmedNoAppliance,
|
|
223
|
+
]);
|
|
224
|
+
/** True when `value` routes a successful check (exact verdict or binary key). */
|
|
225
|
+
function isPositiveInputOutcomeValue(value) {
|
|
226
|
+
return POSITIVE_INPUT_OUTCOMES.has(value) || POSITIVE_DEVICE_TEST_OUTCOMES.has(value);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Validates the input blocks of a step.
|
|
230
|
+
*
|
|
231
|
+
* An {@link EnyoOnboardingV2InputValueType.IpAddress} block is checked more
|
|
232
|
+
* strictly than the other value types because it is the only one the host
|
|
233
|
+
* actually runs a check for: a one-sided outcome set there is a step the
|
|
234
|
+
* installer can enter and never leave, so both "no positive" and "no negative"
|
|
235
|
+
* are errors rather than warnings. Unlike a device test there is no `failed`
|
|
236
|
+
* verdict the host can fall back to.
|
|
237
|
+
*
|
|
238
|
+
* @param step - The step whose blocks are checked.
|
|
239
|
+
* @param at - Human-readable location prefix for messages.
|
|
240
|
+
* @param errors - Collector for blocking problems.
|
|
241
|
+
* @param warnings - Collector for advisory problems.
|
|
242
|
+
*/
|
|
243
|
+
function validateInputBlocks(step, at, errors, warnings) {
|
|
244
|
+
for (const block of step.blocks ?? []) {
|
|
245
|
+
if (block.type !== enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Input)
|
|
246
|
+
continue;
|
|
247
|
+
if (!INPUT_VALUE_TYPES.has(block.valueType)) {
|
|
248
|
+
errors.push(`${at}: input block "${block.id}" has an unknown valueType "${block.valueType}".`);
|
|
249
|
+
}
|
|
250
|
+
if (!block.label?.length)
|
|
251
|
+
errors.push(`${at}: input block "${block.id}" has no label.`);
|
|
252
|
+
if (!block.submitLabel?.length) {
|
|
253
|
+
errors.push(`${at}: input block "${block.id}" has no submitLabel.`);
|
|
254
|
+
}
|
|
255
|
+
const outcomes = block.outcomes ?? [];
|
|
256
|
+
if (outcomes.length < 2) {
|
|
257
|
+
errors.push(`${at}: input block "${block.id}" needs at least 2 outcomes.`);
|
|
258
|
+
}
|
|
259
|
+
const ids = new Set();
|
|
260
|
+
const values = new Set();
|
|
261
|
+
for (const outcome of outcomes) {
|
|
262
|
+
if (ids.has(outcome.id)) {
|
|
263
|
+
errors.push(`${at}: input block "${block.id}" has a duplicate outcome id "${outcome.id}".`);
|
|
264
|
+
}
|
|
265
|
+
ids.add(outcome.id);
|
|
266
|
+
if (values.has(outcome.value)) {
|
|
267
|
+
errors.push(`${at}: input block "${block.id}" wires outcome value "${outcome.value}" more than once.`);
|
|
268
|
+
}
|
|
269
|
+
values.add(outcome.value);
|
|
270
|
+
}
|
|
271
|
+
if (block.valueType === enyo_onboarding_v2_js_1.EnyoOnboardingV2InputValueType.IpAddress) {
|
|
272
|
+
if (![...values].some(isPositiveInputOutcomeValue)) {
|
|
273
|
+
errors.push(`${at}: input block "${block.id}" has no outcome for a successful check — ` +
|
|
274
|
+
`wire "reachable" (or an EnyoDeviceTestOutcomeEnum success member).`);
|
|
275
|
+
}
|
|
276
|
+
if (![...values].some((v) => !isPositiveInputOutcomeValue(v))) {
|
|
277
|
+
errors.push(`${at}: input block "${block.id}" has no outcome for a failed check — ` +
|
|
278
|
+
`the installer would be stranded when the device does not answer.`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else if (outcomes.length > 1) {
|
|
282
|
+
warnings.push(`${at}: input block "${block.id}" has valueType "${block.valueType}", which runs no check — ` +
|
|
283
|
+
`only the positive outcome can ever fire, so its other ${outcomes.length - 1} outcome(s) are dead branches.`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
178
287
|
/**
|
|
179
288
|
* The set of routing-handle keys a step must wire exactly once: one per
|
|
180
|
-
* choice option / action outcome, or the single `continue`
|
|
181
|
-
* has no interactive block.
|
|
289
|
+
* choice option / action outcome / input outcome, or the single `continue`
|
|
290
|
+
* handle when the step has no interactive block.
|
|
291
|
+
*
|
|
292
|
+
* A `Link` block contributes nothing — it is passive content.
|
|
182
293
|
*/
|
|
183
294
|
function requiredHandleKeys(step) {
|
|
184
295
|
const keys = new Set();
|
|
@@ -187,7 +298,8 @@ function requiredHandleKeys(step) {
|
|
|
187
298
|
for (const o of b.options)
|
|
188
299
|
keys.add(`choice:${b.id}:${o.id}`);
|
|
189
300
|
}
|
|
190
|
-
else if (b.type === enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Action
|
|
301
|
+
else if (b.type === enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Action ||
|
|
302
|
+
b.type === enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Input) {
|
|
191
303
|
for (const o of b.outcomes)
|
|
192
304
|
keys.add(`outcome:${b.id}:${o.id}`);
|
|
193
305
|
}
|
|
@@ -10,6 +10,12 @@ var EnyoAirConditioningApplianceAvailableFeaturesEnum;
|
|
|
10
10
|
EnyoAirConditioningApplianceAvailableFeaturesEnum["Cooling"] = "Cooling";
|
|
11
11
|
/** If the air conditioning unit supports heating */
|
|
12
12
|
EnyoAirConditioningApplianceAvailableFeaturesEnum["Heating"] = "Heating";
|
|
13
|
+
/** If the air conditioning unit reports electrical power values (e.g. consumption in watts) */
|
|
14
|
+
EnyoAirConditioningApplianceAvailableFeaturesEnum["Power"] = "Power";
|
|
15
|
+
/** If the air conditioning unit's electrical power draw can be steered (e.g. limited to a target in watts) */
|
|
16
|
+
EnyoAirConditioningApplianceAvailableFeaturesEnum["PowerControllable"] = "PowerControllable";
|
|
17
|
+
/** If the air conditioning unit's target room temperature can be set */
|
|
18
|
+
EnyoAirConditioningApplianceAvailableFeaturesEnum["TemperatureControllable"] = "TemperatureControllable";
|
|
13
19
|
})(EnyoAirConditioningApplianceAvailableFeaturesEnum || (exports.EnyoAirConditioningApplianceAvailableFeaturesEnum = EnyoAirConditioningApplianceAvailableFeaturesEnum = {}));
|
|
14
20
|
/**
|
|
15
21
|
* Energy-optimization modes for an air conditioning appliance. These control how
|
|
@@ -5,7 +5,13 @@ export declare enum EnyoAirConditioningApplianceAvailableFeaturesEnum {
|
|
|
5
5
|
/** If the air conditioning unit supports cooling */
|
|
6
6
|
Cooling = "Cooling",
|
|
7
7
|
/** If the air conditioning unit supports heating */
|
|
8
|
-
Heating = "Heating"
|
|
8
|
+
Heating = "Heating",
|
|
9
|
+
/** If the air conditioning unit reports electrical power values (e.g. consumption in watts) */
|
|
10
|
+
Power = "Power",
|
|
11
|
+
/** If the air conditioning unit's electrical power draw can be steered (e.g. limited to a target in watts) */
|
|
12
|
+
PowerControllable = "PowerControllable",
|
|
13
|
+
/** If the air conditioning unit's target room temperature can be set */
|
|
14
|
+
TemperatureControllable = "TemperatureControllable"
|
|
9
15
|
}
|
|
10
16
|
/**
|
|
11
17
|
* Energy-optimization modes for an air conditioning appliance. These control how
|
|
@@ -11,6 +11,16 @@ var EnyoHeatingRodApplianceAvailableFeaturesEnum;
|
|
|
11
11
|
EnyoHeatingRodApplianceAvailableFeaturesEnum["Power"] = "Power";
|
|
12
12
|
/** If the heating rod supports available power announcements */
|
|
13
13
|
EnyoHeatingRodApplianceAvailableFeaturesEnum["AvailablePowerAnnouncement"] = "AvailablePowerAnnouncement";
|
|
14
|
+
/**
|
|
15
|
+
* If the heating rod has a domestic hot water temperature sensor, i.e. it
|
|
16
|
+
* reports the measured DHW tank temperature rather than only its own
|
|
17
|
+
* operating state.
|
|
18
|
+
*
|
|
19
|
+
* Without this feature {@link EnyoHeatingRodApplianceMetadata.targetTemperatureC}
|
|
20
|
+
* is a setpoint the appliance cannot verify — consumers should not expect a
|
|
21
|
+
* measured temperature to compare it against.
|
|
22
|
+
*/
|
|
23
|
+
EnyoHeatingRodApplianceAvailableFeaturesEnum["DomesticHotWaterSensor"] = "DomesticHotWaterSensor";
|
|
14
24
|
})(EnyoHeatingRodApplianceAvailableFeaturesEnum || (exports.EnyoHeatingRodApplianceAvailableFeaturesEnum = EnyoHeatingRodApplianceAvailableFeaturesEnum = {}));
|
|
15
25
|
/**
|
|
16
26
|
* Operating modes for a heating rod appliance.
|
|
@@ -6,7 +6,17 @@ export declare enum EnyoHeatingRodApplianceAvailableFeaturesEnum {
|
|
|
6
6
|
/** If the heating rod reports electrical power values (e.g. consumption in watts) */
|
|
7
7
|
Power = "Power",
|
|
8
8
|
/** If the heating rod supports available power announcements */
|
|
9
|
-
AvailablePowerAnnouncement = "AvailablePowerAnnouncement"
|
|
9
|
+
AvailablePowerAnnouncement = "AvailablePowerAnnouncement",
|
|
10
|
+
/**
|
|
11
|
+
* If the heating rod has a domestic hot water temperature sensor, i.e. it
|
|
12
|
+
* reports the measured DHW tank temperature rather than only its own
|
|
13
|
+
* operating state.
|
|
14
|
+
*
|
|
15
|
+
* Without this feature {@link EnyoHeatingRodApplianceMetadata.targetTemperatureC}
|
|
16
|
+
* is a setpoint the appliance cannot verify — consumers should not expect a
|
|
17
|
+
* measured temperature to compare it against.
|
|
18
|
+
*/
|
|
19
|
+
DomesticHotWaterSensor = "DomesticHotWaterSensor"
|
|
10
20
|
}
|
|
11
21
|
/**
|
|
12
22
|
* Operating modes for a heating rod appliance.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* before publishing.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.EnyoOnboardingV2TargetType = exports.EnyoOnboardingV2TransitionSourceKind = exports.EnyoOnboardingV2BlockType = exports.EnyoOnboardingV2ChoiceLayout = exports.EnyoOnboardingV2IconKey = exports.EnyoOnboardingV2DeviceSelection = exports.EnyoOnboardingV2ActionKind = exports.EnyoOnboardingV2DynamicKind = exports.EnyoOnboardingV2HintVariant = exports.EnyoOnboardingV2PauseReason = exports.EnyoOnboardingV2StartVariant = void 0;
|
|
25
|
+
exports.EnyoOnboardingV2TargetType = exports.EnyoOnboardingV2TransitionSourceKind = exports.EnyoOnboardingV2BlockType = exports.EnyoOnboardingV2ChoiceLayout = exports.EnyoOnboardingV2IconKey = exports.EnyoOnboardingV2DeviceSelection = exports.EnyoOnboardingV2InputValueType = exports.EnyoOnboardingV2ActionKind = exports.EnyoOnboardingV2DynamicKind = exports.EnyoOnboardingV2HintVariant = exports.EnyoOnboardingV2PauseReason = exports.EnyoOnboardingV2StartVariant = void 0;
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
// Enumerable string enums
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
@@ -88,6 +88,19 @@ var EnyoOnboardingV2ActionKind;
|
|
|
88
88
|
*/
|
|
89
89
|
EnyoOnboardingV2ActionKind["DeviceTest"] = "device-test";
|
|
90
90
|
})(EnyoOnboardingV2ActionKind || (exports.EnyoOnboardingV2ActionKind = EnyoOnboardingV2ActionKind = {}));
|
|
91
|
+
/**
|
|
92
|
+
* What an {@link EnyoOnboardingV2InputBlock} asks the installer for. Drives the
|
|
93
|
+
* keyboard the app shows, the format check it applies and the seeded texts.
|
|
94
|
+
*/
|
|
95
|
+
var EnyoOnboardingV2InputValueType;
|
|
96
|
+
(function (EnyoOnboardingV2InputValueType) {
|
|
97
|
+
/** Free text; any non-empty value is accepted. */
|
|
98
|
+
EnyoOnboardingV2InputValueType["Text"] = "text";
|
|
99
|
+
/** An IPv4 address. This is the only type the host actually checks. */
|
|
100
|
+
EnyoOnboardingV2InputValueType["IpAddress"] = "ip-address";
|
|
101
|
+
/** A number; `,` and `.` are both accepted as the decimal separator. */
|
|
102
|
+
EnyoOnboardingV2InputValueType["Number"] = "number";
|
|
103
|
+
})(EnyoOnboardingV2InputValueType || (exports.EnyoOnboardingV2InputValueType = EnyoOnboardingV2InputValueType = {}));
|
|
91
104
|
/**
|
|
92
105
|
* Which devices a {@link EnyoOnboardingV2ActionKind.DeviceTest} block hands to
|
|
93
106
|
* the app.
|
|
@@ -129,6 +142,8 @@ var EnyoOnboardingV2BlockType;
|
|
|
129
142
|
EnyoOnboardingV2BlockType["Dynamic"] = "dynamic";
|
|
130
143
|
EnyoOnboardingV2BlockType["Choice"] = "choice";
|
|
131
144
|
EnyoOnboardingV2BlockType["Action"] = "action";
|
|
145
|
+
EnyoOnboardingV2BlockType["Link"] = "link";
|
|
146
|
+
EnyoOnboardingV2BlockType["Input"] = "input";
|
|
132
147
|
})(EnyoOnboardingV2BlockType || (exports.EnyoOnboardingV2BlockType = EnyoOnboardingV2BlockType = {}));
|
|
133
148
|
// ---------------------------------------------------------------------------
|
|
134
149
|
// Routing: transitions & targets
|
|
@@ -140,7 +155,7 @@ var EnyoOnboardingV2TransitionSourceKind;
|
|
|
140
155
|
EnyoOnboardingV2TransitionSourceKind["Continue"] = "continue";
|
|
141
156
|
/** A specific option of a `Choice` block was picked. */
|
|
142
157
|
EnyoOnboardingV2TransitionSourceKind["Choice"] = "choice";
|
|
143
|
-
/** A specific outcome of an `Action` block fired. */
|
|
158
|
+
/** A specific outcome of an `Action` or `Input` block fired. */
|
|
144
159
|
EnyoOnboardingV2TransitionSourceKind["Outcome"] = "outcome";
|
|
145
160
|
})(EnyoOnboardingV2TransitionSourceKind || (exports.EnyoOnboardingV2TransitionSourceKind = EnyoOnboardingV2TransitionSourceKind = {}));
|
|
146
161
|
/** Discriminator for the {@link EnyoOnboardingV2Target} union. */
|
|
@@ -78,6 +78,18 @@ export declare enum EnyoOnboardingV2ActionKind {
|
|
|
78
78
|
*/
|
|
79
79
|
DeviceTest = "device-test"
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* What an {@link EnyoOnboardingV2InputBlock} asks the installer for. Drives the
|
|
83
|
+
* keyboard the app shows, the format check it applies and the seeded texts.
|
|
84
|
+
*/
|
|
85
|
+
export declare enum EnyoOnboardingV2InputValueType {
|
|
86
|
+
/** Free text; any non-empty value is accepted. */
|
|
87
|
+
Text = "text",
|
|
88
|
+
/** An IPv4 address. This is the only type the host actually checks. */
|
|
89
|
+
IpAddress = "ip-address",
|
|
90
|
+
/** A number; `,` and `.` are both accepted as the decimal separator. */
|
|
91
|
+
Number = "number"
|
|
92
|
+
}
|
|
81
93
|
/**
|
|
82
94
|
* Which devices a {@link EnyoOnboardingV2ActionKind.DeviceTest} block hands to
|
|
83
95
|
* the app.
|
|
@@ -111,7 +123,9 @@ export declare enum EnyoOnboardingV2BlockType {
|
|
|
111
123
|
Hint = "hint",
|
|
112
124
|
Dynamic = "dynamic",
|
|
113
125
|
Choice = "choice",
|
|
114
|
-
Action = "action"
|
|
126
|
+
Action = "action",
|
|
127
|
+
Link = "link",
|
|
128
|
+
Input = "input"
|
|
115
129
|
}
|
|
116
130
|
/** Fields shared by every content/interactive block. */
|
|
117
131
|
export interface EnyoOnboardingV2BlockBase {
|
|
@@ -209,17 +223,139 @@ export interface EnyoOnboardingV2ActionBlock extends EnyoOnboardingV2BlockBase {
|
|
|
209
223
|
*/
|
|
210
224
|
deviceSelection?: EnyoOnboardingV2DeviceSelection;
|
|
211
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* A fixed URL the installer opens or copies — a vendor portal, a manual, a
|
|
228
|
+
* firmware download.
|
|
229
|
+
*
|
|
230
|
+
* Passive content: it produces no routing handle, so a step whose only
|
|
231
|
+
* non-content block is a link still routes through its single `continue`
|
|
232
|
+
* handle. Unlike {@link EnyoOnboardingV2DynamicBlock}, the URL is the same for
|
|
233
|
+
* every installer — nothing is resolved per device.
|
|
234
|
+
*/
|
|
235
|
+
export interface EnyoOnboardingV2LinkBlock extends EnyoOnboardingV2BlockBase {
|
|
236
|
+
type: EnyoOnboardingV2BlockType.Link;
|
|
237
|
+
/**
|
|
238
|
+
* Absolute `http(s)` URL, not translated. Any other scheme is rejected by
|
|
239
|
+
* the validator and by the host: the installer app renders this as a tap
|
|
240
|
+
* target.
|
|
241
|
+
*/
|
|
242
|
+
url: string;
|
|
243
|
+
/** Translated link text (de/en). Falls back to the raw URL when empty. */
|
|
244
|
+
label: EnyoOnboardingTranslatedContent[];
|
|
245
|
+
/** Optional translated context line below the link (de/en). */
|
|
246
|
+
description?: EnyoOnboardingTranslatedContent[];
|
|
247
|
+
/**
|
|
248
|
+
* Show a copy-to-clipboard button. Defaults to `true` — on a phone it is the
|
|
249
|
+
* more useful affordance of the two.
|
|
250
|
+
*/
|
|
251
|
+
copyable?: boolean;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* A possible verdict of an {@link EnyoOnboardingV2InputBlock}'s check; each
|
|
255
|
+
* outcome is a routing handle.
|
|
256
|
+
*/
|
|
257
|
+
export interface EnyoOnboardingV2InputOutcome {
|
|
258
|
+
/** Stable id, unique within the block; referenced by a transition. */
|
|
259
|
+
id: string;
|
|
260
|
+
/**
|
|
261
|
+
* Semantic key, not translated. Either an {@link EnyoDeviceTestOutcomeEnum}
|
|
262
|
+
* member (routed exactly) or one of the binary keys the host collapses
|
|
263
|
+
* onto — `reachable` / `unreachable`. See {@link EnyoOnboardingV2InputBlock}
|
|
264
|
+
* for the full mapping.
|
|
265
|
+
*/
|
|
266
|
+
value: string;
|
|
267
|
+
/** Translated display label for the verdict (de/en). */
|
|
268
|
+
label: EnyoOnboardingTranslatedContent[];
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* The installer types a value and the **host** checks it, producing the branch —
|
|
272
|
+
* unlike {@link EnyoOnboardingV2ChoiceBlock}, where the installer picks the
|
|
273
|
+
* branch directly.
|
|
274
|
+
*
|
|
275
|
+
* For {@link EnyoOnboardingV2InputValueType.IpAddress} the host pairs the typed
|
|
276
|
+
* address with this energy app (creating the network device when the scan never
|
|
277
|
+
* discovered it) and runs the app's registered {@link EnyoDeviceTestHandler}
|
|
278
|
+
* against it, so the verdict is the app's own — it confirms *your* device, not
|
|
279
|
+
* merely a live host. `Text` and `Number` are recorded and take the positive
|
|
280
|
+
* branch; there is nothing the host could check about them.
|
|
281
|
+
*
|
|
282
|
+
* Each outcome MUST have exactly one outgoing transition.
|
|
283
|
+
*
|
|
284
|
+
* **How a verdict picks an outcome**
|
|
285
|
+
*
|
|
286
|
+
* 1. *Exact match wins.* An outcome whose `value` is an
|
|
287
|
+
* {@link EnyoDeviceTestOutcomeEnum} member receives that verdict verbatim —
|
|
288
|
+
* so a guide can route `authentication-required` to a credentials step and
|
|
289
|
+
* `user-action-required` to a "press the pairing button" step.
|
|
290
|
+
* 2. *Otherwise it collapses to a binary pair.* `appliances-created`,
|
|
291
|
+
* `appliances-already-existed` and `device-confirmed-no-appliance` go to the
|
|
292
|
+
* first outcome valued `reachable` / `success` / `found`; everything else
|
|
293
|
+
* (`unreachable`, `not-supported`, `authentication-required`,
|
|
294
|
+
* `access-not-granted`, `user-action-required`, `failed`) goes to the first
|
|
295
|
+
* outcome valued `unreachable` / `failure` / `failed` / `not-found`.
|
|
296
|
+
* 3. `Text` and `Number` run no check: the value is recorded and the flow takes
|
|
297
|
+
* the positive outcome (first `reachable`/`success`/`found`, else the first
|
|
298
|
+
* outcome). Any further outcome on such a block can never fire.
|
|
299
|
+
* 4. An offline hub, a package that is not installed, or no energy app linked to
|
|
300
|
+
* the vendor/model all resolve to the **negative** branch — never an error.
|
|
301
|
+
* The guide always gets an outcome to route on.
|
|
302
|
+
*
|
|
303
|
+
* The typed value is persisted per block in the run state and handed back on
|
|
304
|
+
* resume/back, so a typo is corrected rather than retyped. Values may be secrets
|
|
305
|
+
* (a device password typed into a `Text` input): the host logs outcome keys
|
|
306
|
+
* only, never the value itself.
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```ts
|
|
310
|
+
* onboardingV2Block.input(
|
|
311
|
+
* 'b3',
|
|
312
|
+
* EnyoOnboardingV2InputValueType.IpAddress,
|
|
313
|
+
* t('IP-Adresse des Geräts', 'Device IP address'),
|
|
314
|
+
* t('Gerät prüfen', 'Check device'),
|
|
315
|
+
* [
|
|
316
|
+
* {id: 'ok', value: 'reachable', label: t('Gerät erreichbar', 'Device reachable')},
|
|
317
|
+
* {
|
|
318
|
+
* id: 'auth',
|
|
319
|
+
* value: EnyoDeviceTestOutcomeEnum.AuthenticationRequired,
|
|
320
|
+
* label: t('Passwort nötig', 'Password required'),
|
|
321
|
+
* },
|
|
322
|
+
* {id: 'no', value: 'unreachable', label: t('Nicht erreichbar', 'Not reachable')},
|
|
323
|
+
* ],
|
|
324
|
+
* {placeholder: t('z. B. 192.168.1.42', 'e.g. 192.168.1.42')},
|
|
325
|
+
* );
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
export interface EnyoOnboardingV2InputBlock extends EnyoOnboardingV2BlockBase {
|
|
329
|
+
type: EnyoOnboardingV2BlockType.Input;
|
|
330
|
+
/** What is asked for; drives keyboard, format check and seeded texts. */
|
|
331
|
+
valueType: EnyoOnboardingV2InputValueType;
|
|
332
|
+
/** Translated field label, e.g. "IP-Adresse des Geräts" (de/en). */
|
|
333
|
+
label: EnyoOnboardingTranslatedContent[];
|
|
334
|
+
/** Optional translated placeholder, e.g. "z. B. 192.168.1.42" (de/en). */
|
|
335
|
+
placeholder?: EnyoOnboardingTranslatedContent[];
|
|
336
|
+
/** Optional translated help text — where the installer finds the value (de/en). */
|
|
337
|
+
help?: EnyoOnboardingTranslatedContent[];
|
|
338
|
+
/** Translated submit button text, e.g. "Gerät prüfen" (de/en). */
|
|
339
|
+
submitLabel: EnyoOnboardingTranslatedContent[];
|
|
340
|
+
/** The possible verdicts; each is a routing handle. At least 2. */
|
|
341
|
+
outcomes: EnyoOnboardingV2InputOutcome[];
|
|
342
|
+
}
|
|
212
343
|
/** Any block that can appear in a step's `blocks`. */
|
|
213
|
-
export type EnyoOnboardingV2Block = EnyoOnboardingV2TextBlock | EnyoOnboardingV2HeadlineBlock | EnyoOnboardingV2BulletsBlock | EnyoOnboardingV2ImageBlock | EnyoOnboardingV2HintBlock | EnyoOnboardingV2DynamicBlock | EnyoOnboardingV2ChoiceBlock | EnyoOnboardingV2ActionBlock;
|
|
214
|
-
/**
|
|
215
|
-
|
|
344
|
+
export type EnyoOnboardingV2Block = EnyoOnboardingV2TextBlock | EnyoOnboardingV2HeadlineBlock | EnyoOnboardingV2BulletsBlock | EnyoOnboardingV2ImageBlock | EnyoOnboardingV2HintBlock | EnyoOnboardingV2DynamicBlock | EnyoOnboardingV2ChoiceBlock | EnyoOnboardingV2ActionBlock | EnyoOnboardingV2LinkBlock | EnyoOnboardingV2InputBlock;
|
|
345
|
+
/**
|
|
346
|
+
* Blocks that produce routing handles (a step's decision points).
|
|
347
|
+
*
|
|
348
|
+
* A {@link EnyoOnboardingV2LinkBlock} is deliberately absent: it is passive
|
|
349
|
+
* content and routes nothing.
|
|
350
|
+
*/
|
|
351
|
+
export type EnyoOnboardingV2InteractiveBlock = EnyoOnboardingV2ChoiceBlock | EnyoOnboardingV2ActionBlock | EnyoOnboardingV2InputBlock;
|
|
216
352
|
/** Discriminator for the {@link EnyoOnboardingV2TransitionSource} union. */
|
|
217
353
|
export declare enum EnyoOnboardingV2TransitionSourceKind {
|
|
218
354
|
/** The step's plain "continue" button (steps with no interactive block). */
|
|
219
355
|
Continue = "continue",
|
|
220
356
|
/** A specific option of a `Choice` block was picked. */
|
|
221
357
|
Choice = "choice",
|
|
222
|
-
/** A specific outcome of an `Action` block fired. */
|
|
358
|
+
/** A specific outcome of an `Action` or `Input` block fired. */
|
|
223
359
|
Outcome = "outcome"
|
|
224
360
|
}
|
|
225
361
|
/** Where a transition leaves from within a step. */
|
|
@@ -234,7 +370,7 @@ export type EnyoOnboardingV2TransitionSource =
|
|
|
234
370
|
blockId: string;
|
|
235
371
|
optionId: string;
|
|
236
372
|
}
|
|
237
|
-
/** A specific outcome of an `Action` block fired. */
|
|
373
|
+
/** A specific outcome of an `Action` or `Input` block fired. */
|
|
238
374
|
| {
|
|
239
375
|
kind: EnyoOnboardingV2TransitionSourceKind.Outcome;
|
|
240
376
|
blockId: string;
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
|
|
|
9
9
|
/**
|
|
10
10
|
* Current version of the enyo Energy App SDK.
|
|
11
11
|
*/
|
|
12
|
-
exports.SDK_VERSION = '0.0.
|
|
12
|
+
exports.SDK_VERSION = '0.0.188';
|
|
13
13
|
/**
|
|
14
14
|
* Gets the current SDK version.
|
|
15
15
|
* @returns The semantic version string of the SDK
|
package/dist/cjs/version.d.cts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { EnyoOnboardingTranslatedContent } from '../../types/enyo-onboarding.js';
|
|
13
13
|
import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2ChoiceLayout, EnyoOnboardingV2DeviceSelection } from '../../types/enyo-onboarding-v2.js';
|
|
14
|
-
import type { EnyoOnboardingV2ActionOutcome, EnyoOnboardingV2Block, EnyoOnboardingV2ChoiceOption, EnyoOnboardingV2DynamicKind, EnyoOnboardingV2Guide, EnyoOnboardingV2HintVariant, EnyoOnboardingV2PauseReason, EnyoOnboardingV2StartVariant, EnyoOnboardingV2Target, EnyoOnboardingV2Transition } from '../../types/enyo-onboarding-v2.js';
|
|
14
|
+
import type { EnyoOnboardingV2ActionOutcome, EnyoOnboardingV2Block, EnyoOnboardingV2ChoiceOption, EnyoOnboardingV2DynamicKind, EnyoOnboardingV2Guide, EnyoOnboardingV2HintVariant, EnyoOnboardingV2InputOutcome, EnyoOnboardingV2InputValueType, EnyoOnboardingV2PauseReason, EnyoOnboardingV2StartVariant, EnyoOnboardingV2Target, EnyoOnboardingV2Transition } from '../../types/enyo-onboarding-v2.js';
|
|
15
15
|
/**
|
|
16
16
|
* Identity helper that type-checks a v2 guide literal at definition time
|
|
17
17
|
* (mirrors `defineEnergyAppPackage`). Prefer this over a bare object literal so
|
|
@@ -98,6 +98,41 @@ export declare const onboardingV2Block: {
|
|
|
98
98
|
* @param deviceSelection - Which devices to test (defaults to `Detected`).
|
|
99
99
|
*/
|
|
100
100
|
deviceTest: (id: string, label: EnyoOnboardingTranslatedContent[], outcomes: EnyoOnboardingV2ActionOutcome[], deviceSelection?: EnyoOnboardingV2DeviceSelection) => EnyoOnboardingV2Block;
|
|
101
|
+
/**
|
|
102
|
+
* A link block: a fixed URL the installer opens or copies.
|
|
103
|
+
*
|
|
104
|
+
* Passive content — it produces no routing handle, so a step whose only
|
|
105
|
+
* non-content block is a link still routes through `continue`.
|
|
106
|
+
*
|
|
107
|
+
* @param id - Stable block id, unique within the guide.
|
|
108
|
+
* @param url - Absolute `http(s)` URL. Other schemes are rejected by the validator.
|
|
109
|
+
* @param label - Translated link text (de/en).
|
|
110
|
+
* @param opts - Optional translated `description` and `copyable` (defaults to `true`).
|
|
111
|
+
*/
|
|
112
|
+
link: (id: string, url: string, label: EnyoOnboardingTranslatedContent[], opts?: {
|
|
113
|
+
description?: EnyoOnboardingTranslatedContent[];
|
|
114
|
+
copyable?: boolean;
|
|
115
|
+
}) => EnyoOnboardingV2Block;
|
|
116
|
+
/**
|
|
117
|
+
* An input block: the installer types a value and the host checks it,
|
|
118
|
+
* producing the branch.
|
|
119
|
+
*
|
|
120
|
+
* For {@link EnyoOnboardingV2InputValueType.IpAddress} the host runs this
|
|
121
|
+
* app's registered device-test handler against the typed address; see
|
|
122
|
+
* {@link EnyoOnboardingV2InputBlock} for how a verdict picks an outcome.
|
|
123
|
+
* Route the outcomes with {@link onOutcomeV2} — there is no separate helper.
|
|
124
|
+
*
|
|
125
|
+
* @param id - Stable block id, unique within the guide.
|
|
126
|
+
* @param valueType - What is asked for (`Text` | `IpAddress` | `Number`).
|
|
127
|
+
* @param label - Translated field label (de/en).
|
|
128
|
+
* @param submitLabel - Translated submit button text (de/en).
|
|
129
|
+
* @param outcomes - The possible verdicts; each is a routing handle.
|
|
130
|
+
* @param opts - Optional translated `placeholder` and `help`.
|
|
131
|
+
*/
|
|
132
|
+
input: (id: string, valueType: EnyoOnboardingV2InputValueType, label: EnyoOnboardingTranslatedContent[], submitLabel: EnyoOnboardingTranslatedContent[], outcomes: EnyoOnboardingV2InputOutcome[], opts?: {
|
|
133
|
+
placeholder?: EnyoOnboardingTranslatedContent[];
|
|
134
|
+
help?: EnyoOnboardingTranslatedContent[];
|
|
135
|
+
}) => EnyoOnboardingV2Block;
|
|
101
136
|
};
|
|
102
137
|
/** Typed factories for each transition {@link EnyoOnboardingV2Target}. */
|
|
103
138
|
export declare const onboardingV2Target: {
|
|
@@ -126,6 +126,51 @@ export const onboardingV2Block = {
|
|
|
126
126
|
outcomes,
|
|
127
127
|
deviceSelection,
|
|
128
128
|
}),
|
|
129
|
+
/**
|
|
130
|
+
* A link block: a fixed URL the installer opens or copies.
|
|
131
|
+
*
|
|
132
|
+
* Passive content — it produces no routing handle, so a step whose only
|
|
133
|
+
* non-content block is a link still routes through `continue`.
|
|
134
|
+
*
|
|
135
|
+
* @param id - Stable block id, unique within the guide.
|
|
136
|
+
* @param url - Absolute `http(s)` URL. Other schemes are rejected by the validator.
|
|
137
|
+
* @param label - Translated link text (de/en).
|
|
138
|
+
* @param opts - Optional translated `description` and `copyable` (defaults to `true`).
|
|
139
|
+
*/
|
|
140
|
+
link: (id, url, label, opts) => ({
|
|
141
|
+
id,
|
|
142
|
+
type: EnyoOnboardingV2BlockType.Link,
|
|
143
|
+
url,
|
|
144
|
+
label,
|
|
145
|
+
description: opts?.description,
|
|
146
|
+
copyable: opts?.copyable ?? true,
|
|
147
|
+
}),
|
|
148
|
+
/**
|
|
149
|
+
* An input block: the installer types a value and the host checks it,
|
|
150
|
+
* producing the branch.
|
|
151
|
+
*
|
|
152
|
+
* For {@link EnyoOnboardingV2InputValueType.IpAddress} the host runs this
|
|
153
|
+
* app's registered device-test handler against the typed address; see
|
|
154
|
+
* {@link EnyoOnboardingV2InputBlock} for how a verdict picks an outcome.
|
|
155
|
+
* Route the outcomes with {@link onOutcomeV2} — there is no separate helper.
|
|
156
|
+
*
|
|
157
|
+
* @param id - Stable block id, unique within the guide.
|
|
158
|
+
* @param valueType - What is asked for (`Text` | `IpAddress` | `Number`).
|
|
159
|
+
* @param label - Translated field label (de/en).
|
|
160
|
+
* @param submitLabel - Translated submit button text (de/en).
|
|
161
|
+
* @param outcomes - The possible verdicts; each is a routing handle.
|
|
162
|
+
* @param opts - Optional translated `placeholder` and `help`.
|
|
163
|
+
*/
|
|
164
|
+
input: (id, valueType, label, submitLabel, outcomes, opts) => ({
|
|
165
|
+
id,
|
|
166
|
+
type: EnyoOnboardingV2BlockType.Input,
|
|
167
|
+
valueType,
|
|
168
|
+
label,
|
|
169
|
+
submitLabel,
|
|
170
|
+
outcomes,
|
|
171
|
+
placeholder: opts?.placeholder,
|
|
172
|
+
help: opts?.help,
|
|
173
|
+
}),
|
|
129
174
|
};
|
|
130
175
|
// ---------------------------------------------------------------------------
|
|
131
176
|
// Target factories
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* path to success). Use {@link validateOnboardingGuideV2} for the non-throwing
|
|
8
8
|
* result, or {@link assertValidOnboardingGuideV2} to throw on the first failure.
|
|
9
9
|
*/
|
|
10
|
-
import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2BlockType, EnyoOnboardingV2TargetType, EnyoOnboardingV2TransitionSourceKind, } from '../../types/enyo-onboarding-v2.js';
|
|
10
|
+
import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2BlockType, EnyoOnboardingV2InputValueType, EnyoOnboardingV2TargetType, EnyoOnboardingV2TransitionSourceKind, } from '../../types/enyo-onboarding-v2.js';
|
|
11
11
|
import { EnyoDeviceTestOutcomeEnum } from '../../types/enyo-device-test.js';
|
|
12
12
|
/**
|
|
13
13
|
* Thrown by {@link assertValidOnboardingGuideV2} when a guide fails validation.
|
|
@@ -64,6 +64,8 @@ export function validateOnboardingGuideV2(guide) {
|
|
|
64
64
|
if (!step.blocks?.length)
|
|
65
65
|
warnings.push(`${at}: no content blocks.`);
|
|
66
66
|
validateActionBlocks(step, at, errors, warnings);
|
|
67
|
+
validateLinkBlocks(step, at, errors, warnings);
|
|
68
|
+
validateInputBlocks(step, at, errors, warnings);
|
|
67
69
|
}
|
|
68
70
|
if (!guide.startStepId || !stepIds.has(guide.startStepId)) {
|
|
69
71
|
errors.push('`startStepId` must reference an existing step.');
|
|
@@ -169,10 +171,119 @@ function validateActionBlocks(step, at, errors, warnings) {
|
|
|
169
171
|
}
|
|
170
172
|
}
|
|
171
173
|
}
|
|
174
|
+
/** An absolute `http(s)` URL — the only scheme a link block may carry. */
|
|
175
|
+
const HTTP_URL_RE = /^https?:\/\/\S+$/i;
|
|
176
|
+
/**
|
|
177
|
+
* Validates the link blocks of a step.
|
|
178
|
+
*
|
|
179
|
+
* The scheme check is **security-relevant, not cosmetic**: the installer app
|
|
180
|
+
* renders a link block as a tap target, so this keeps `javascript:`, `data:` and
|
|
181
|
+
* `file:` payloads out of it. connect-core enforces the identical rule
|
|
182
|
+
* server-side and the app re-checks before opening — three gates, deliberately.
|
|
183
|
+
* Do not relax it to a generic "looks like a URL" test.
|
|
184
|
+
*
|
|
185
|
+
* @param step - The step whose blocks are checked.
|
|
186
|
+
* @param at - Human-readable location prefix for messages.
|
|
187
|
+
* @param errors - Collector for blocking problems.
|
|
188
|
+
* @param warnings - Collector for advisory problems.
|
|
189
|
+
*/
|
|
190
|
+
function validateLinkBlocks(step, at, errors, warnings) {
|
|
191
|
+
for (const block of step.blocks ?? []) {
|
|
192
|
+
if (block.type !== EnyoOnboardingV2BlockType.Link)
|
|
193
|
+
continue;
|
|
194
|
+
if (!block.url?.trim()) {
|
|
195
|
+
errors.push(`${at}: link block "${block.id}" has no url.`);
|
|
196
|
+
}
|
|
197
|
+
else if (!HTTP_URL_RE.test(block.url.trim())) {
|
|
198
|
+
errors.push(`${at}: link block "${block.id}" url must be an absolute http(s) URL.`);
|
|
199
|
+
}
|
|
200
|
+
if (!block.label?.length) {
|
|
201
|
+
warnings.push(`${at}: link block "${block.id}" has no label — the raw URL is shown instead.`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** Every valid {@link EnyoOnboardingV2InputValueType} value. */
|
|
206
|
+
const INPUT_VALUE_TYPES = new Set(Object.values(EnyoOnboardingV2InputValueType));
|
|
207
|
+
/** Outcome values the host treats as "the check succeeded". */
|
|
208
|
+
const POSITIVE_INPUT_OUTCOMES = new Set(['reachable', 'success', 'found']);
|
|
209
|
+
/**
|
|
210
|
+
* The {@link EnyoDeviceTestOutcomeEnum} verdicts that collapse onto a positive
|
|
211
|
+
* input outcome; every other verdict collapses onto a negative one.
|
|
212
|
+
*/
|
|
213
|
+
const POSITIVE_DEVICE_TEST_OUTCOMES = new Set([
|
|
214
|
+
EnyoDeviceTestOutcomeEnum.AppliancesCreated,
|
|
215
|
+
EnyoDeviceTestOutcomeEnum.AppliancesAlreadyExisted,
|
|
216
|
+
EnyoDeviceTestOutcomeEnum.DeviceConfirmedNoAppliance,
|
|
217
|
+
]);
|
|
218
|
+
/** True when `value` routes a successful check (exact verdict or binary key). */
|
|
219
|
+
function isPositiveInputOutcomeValue(value) {
|
|
220
|
+
return POSITIVE_INPUT_OUTCOMES.has(value) || POSITIVE_DEVICE_TEST_OUTCOMES.has(value);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Validates the input blocks of a step.
|
|
224
|
+
*
|
|
225
|
+
* An {@link EnyoOnboardingV2InputValueType.IpAddress} block is checked more
|
|
226
|
+
* strictly than the other value types because it is the only one the host
|
|
227
|
+
* actually runs a check for: a one-sided outcome set there is a step the
|
|
228
|
+
* installer can enter and never leave, so both "no positive" and "no negative"
|
|
229
|
+
* are errors rather than warnings. Unlike a device test there is no `failed`
|
|
230
|
+
* verdict the host can fall back to.
|
|
231
|
+
*
|
|
232
|
+
* @param step - The step whose blocks are checked.
|
|
233
|
+
* @param at - Human-readable location prefix for messages.
|
|
234
|
+
* @param errors - Collector for blocking problems.
|
|
235
|
+
* @param warnings - Collector for advisory problems.
|
|
236
|
+
*/
|
|
237
|
+
function validateInputBlocks(step, at, errors, warnings) {
|
|
238
|
+
for (const block of step.blocks ?? []) {
|
|
239
|
+
if (block.type !== EnyoOnboardingV2BlockType.Input)
|
|
240
|
+
continue;
|
|
241
|
+
if (!INPUT_VALUE_TYPES.has(block.valueType)) {
|
|
242
|
+
errors.push(`${at}: input block "${block.id}" has an unknown valueType "${block.valueType}".`);
|
|
243
|
+
}
|
|
244
|
+
if (!block.label?.length)
|
|
245
|
+
errors.push(`${at}: input block "${block.id}" has no label.`);
|
|
246
|
+
if (!block.submitLabel?.length) {
|
|
247
|
+
errors.push(`${at}: input block "${block.id}" has no submitLabel.`);
|
|
248
|
+
}
|
|
249
|
+
const outcomes = block.outcomes ?? [];
|
|
250
|
+
if (outcomes.length < 2) {
|
|
251
|
+
errors.push(`${at}: input block "${block.id}" needs at least 2 outcomes.`);
|
|
252
|
+
}
|
|
253
|
+
const ids = new Set();
|
|
254
|
+
const values = new Set();
|
|
255
|
+
for (const outcome of outcomes) {
|
|
256
|
+
if (ids.has(outcome.id)) {
|
|
257
|
+
errors.push(`${at}: input block "${block.id}" has a duplicate outcome id "${outcome.id}".`);
|
|
258
|
+
}
|
|
259
|
+
ids.add(outcome.id);
|
|
260
|
+
if (values.has(outcome.value)) {
|
|
261
|
+
errors.push(`${at}: input block "${block.id}" wires outcome value "${outcome.value}" more than once.`);
|
|
262
|
+
}
|
|
263
|
+
values.add(outcome.value);
|
|
264
|
+
}
|
|
265
|
+
if (block.valueType === EnyoOnboardingV2InputValueType.IpAddress) {
|
|
266
|
+
if (![...values].some(isPositiveInputOutcomeValue)) {
|
|
267
|
+
errors.push(`${at}: input block "${block.id}" has no outcome for a successful check — ` +
|
|
268
|
+
`wire "reachable" (or an EnyoDeviceTestOutcomeEnum success member).`);
|
|
269
|
+
}
|
|
270
|
+
if (![...values].some((v) => !isPositiveInputOutcomeValue(v))) {
|
|
271
|
+
errors.push(`${at}: input block "${block.id}" has no outcome for a failed check — ` +
|
|
272
|
+
`the installer would be stranded when the device does not answer.`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else if (outcomes.length > 1) {
|
|
276
|
+
warnings.push(`${at}: input block "${block.id}" has valueType "${block.valueType}", which runs no check — ` +
|
|
277
|
+
`only the positive outcome can ever fire, so its other ${outcomes.length - 1} outcome(s) are dead branches.`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
172
281
|
/**
|
|
173
282
|
* The set of routing-handle keys a step must wire exactly once: one per
|
|
174
|
-
* choice option / action outcome, or the single `continue`
|
|
175
|
-
* has no interactive block.
|
|
283
|
+
* choice option / action outcome / input outcome, or the single `continue`
|
|
284
|
+
* handle when the step has no interactive block.
|
|
285
|
+
*
|
|
286
|
+
* A `Link` block contributes nothing — it is passive content.
|
|
176
287
|
*/
|
|
177
288
|
function requiredHandleKeys(step) {
|
|
178
289
|
const keys = new Set();
|
|
@@ -181,7 +292,8 @@ function requiredHandleKeys(step) {
|
|
|
181
292
|
for (const o of b.options)
|
|
182
293
|
keys.add(`choice:${b.id}:${o.id}`);
|
|
183
294
|
}
|
|
184
|
-
else if (b.type === EnyoOnboardingV2BlockType.Action
|
|
295
|
+
else if (b.type === EnyoOnboardingV2BlockType.Action ||
|
|
296
|
+
b.type === EnyoOnboardingV2BlockType.Input) {
|
|
185
297
|
for (const o of b.outcomes)
|
|
186
298
|
keys.add(`outcome:${b.id}:${o.id}`);
|
|
187
299
|
}
|
|
@@ -5,7 +5,13 @@ export declare enum EnyoAirConditioningApplianceAvailableFeaturesEnum {
|
|
|
5
5
|
/** If the air conditioning unit supports cooling */
|
|
6
6
|
Cooling = "Cooling",
|
|
7
7
|
/** If the air conditioning unit supports heating */
|
|
8
|
-
Heating = "Heating"
|
|
8
|
+
Heating = "Heating",
|
|
9
|
+
/** If the air conditioning unit reports electrical power values (e.g. consumption in watts) */
|
|
10
|
+
Power = "Power",
|
|
11
|
+
/** If the air conditioning unit's electrical power draw can be steered (e.g. limited to a target in watts) */
|
|
12
|
+
PowerControllable = "PowerControllable",
|
|
13
|
+
/** If the air conditioning unit's target room temperature can be set */
|
|
14
|
+
TemperatureControllable = "TemperatureControllable"
|
|
9
15
|
}
|
|
10
16
|
/**
|
|
11
17
|
* Energy-optimization modes for an air conditioning appliance. These control how
|
|
@@ -7,6 +7,12 @@ export var EnyoAirConditioningApplianceAvailableFeaturesEnum;
|
|
|
7
7
|
EnyoAirConditioningApplianceAvailableFeaturesEnum["Cooling"] = "Cooling";
|
|
8
8
|
/** If the air conditioning unit supports heating */
|
|
9
9
|
EnyoAirConditioningApplianceAvailableFeaturesEnum["Heating"] = "Heating";
|
|
10
|
+
/** If the air conditioning unit reports electrical power values (e.g. consumption in watts) */
|
|
11
|
+
EnyoAirConditioningApplianceAvailableFeaturesEnum["Power"] = "Power";
|
|
12
|
+
/** If the air conditioning unit's electrical power draw can be steered (e.g. limited to a target in watts) */
|
|
13
|
+
EnyoAirConditioningApplianceAvailableFeaturesEnum["PowerControllable"] = "PowerControllable";
|
|
14
|
+
/** If the air conditioning unit's target room temperature can be set */
|
|
15
|
+
EnyoAirConditioningApplianceAvailableFeaturesEnum["TemperatureControllable"] = "TemperatureControllable";
|
|
10
16
|
})(EnyoAirConditioningApplianceAvailableFeaturesEnum || (EnyoAirConditioningApplianceAvailableFeaturesEnum = {}));
|
|
11
17
|
/**
|
|
12
18
|
* Energy-optimization modes for an air conditioning appliance. These control how
|
|
@@ -6,7 +6,17 @@ export declare enum EnyoHeatingRodApplianceAvailableFeaturesEnum {
|
|
|
6
6
|
/** If the heating rod reports electrical power values (e.g. consumption in watts) */
|
|
7
7
|
Power = "Power",
|
|
8
8
|
/** If the heating rod supports available power announcements */
|
|
9
|
-
AvailablePowerAnnouncement = "AvailablePowerAnnouncement"
|
|
9
|
+
AvailablePowerAnnouncement = "AvailablePowerAnnouncement",
|
|
10
|
+
/**
|
|
11
|
+
* If the heating rod has a domestic hot water temperature sensor, i.e. it
|
|
12
|
+
* reports the measured DHW tank temperature rather than only its own
|
|
13
|
+
* operating state.
|
|
14
|
+
*
|
|
15
|
+
* Without this feature {@link EnyoHeatingRodApplianceMetadata.targetTemperatureC}
|
|
16
|
+
* is a setpoint the appliance cannot verify — consumers should not expect a
|
|
17
|
+
* measured temperature to compare it against.
|
|
18
|
+
*/
|
|
19
|
+
DomesticHotWaterSensor = "DomesticHotWaterSensor"
|
|
10
20
|
}
|
|
11
21
|
/**
|
|
12
22
|
* Operating modes for a heating rod appliance.
|
|
@@ -8,6 +8,16 @@ export var EnyoHeatingRodApplianceAvailableFeaturesEnum;
|
|
|
8
8
|
EnyoHeatingRodApplianceAvailableFeaturesEnum["Power"] = "Power";
|
|
9
9
|
/** If the heating rod supports available power announcements */
|
|
10
10
|
EnyoHeatingRodApplianceAvailableFeaturesEnum["AvailablePowerAnnouncement"] = "AvailablePowerAnnouncement";
|
|
11
|
+
/**
|
|
12
|
+
* If the heating rod has a domestic hot water temperature sensor, i.e. it
|
|
13
|
+
* reports the measured DHW tank temperature rather than only its own
|
|
14
|
+
* operating state.
|
|
15
|
+
*
|
|
16
|
+
* Without this feature {@link EnyoHeatingRodApplianceMetadata.targetTemperatureC}
|
|
17
|
+
* is a setpoint the appliance cannot verify — consumers should not expect a
|
|
18
|
+
* measured temperature to compare it against.
|
|
19
|
+
*/
|
|
20
|
+
EnyoHeatingRodApplianceAvailableFeaturesEnum["DomesticHotWaterSensor"] = "DomesticHotWaterSensor";
|
|
11
21
|
})(EnyoHeatingRodApplianceAvailableFeaturesEnum || (EnyoHeatingRodApplianceAvailableFeaturesEnum = {}));
|
|
12
22
|
/**
|
|
13
23
|
* Operating modes for a heating rod appliance.
|
|
@@ -78,6 +78,18 @@ export declare enum EnyoOnboardingV2ActionKind {
|
|
|
78
78
|
*/
|
|
79
79
|
DeviceTest = "device-test"
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* What an {@link EnyoOnboardingV2InputBlock} asks the installer for. Drives the
|
|
83
|
+
* keyboard the app shows, the format check it applies and the seeded texts.
|
|
84
|
+
*/
|
|
85
|
+
export declare enum EnyoOnboardingV2InputValueType {
|
|
86
|
+
/** Free text; any non-empty value is accepted. */
|
|
87
|
+
Text = "text",
|
|
88
|
+
/** An IPv4 address. This is the only type the host actually checks. */
|
|
89
|
+
IpAddress = "ip-address",
|
|
90
|
+
/** A number; `,` and `.` are both accepted as the decimal separator. */
|
|
91
|
+
Number = "number"
|
|
92
|
+
}
|
|
81
93
|
/**
|
|
82
94
|
* Which devices a {@link EnyoOnboardingV2ActionKind.DeviceTest} block hands to
|
|
83
95
|
* the app.
|
|
@@ -111,7 +123,9 @@ export declare enum EnyoOnboardingV2BlockType {
|
|
|
111
123
|
Hint = "hint",
|
|
112
124
|
Dynamic = "dynamic",
|
|
113
125
|
Choice = "choice",
|
|
114
|
-
Action = "action"
|
|
126
|
+
Action = "action",
|
|
127
|
+
Link = "link",
|
|
128
|
+
Input = "input"
|
|
115
129
|
}
|
|
116
130
|
/** Fields shared by every content/interactive block. */
|
|
117
131
|
export interface EnyoOnboardingV2BlockBase {
|
|
@@ -209,17 +223,139 @@ export interface EnyoOnboardingV2ActionBlock extends EnyoOnboardingV2BlockBase {
|
|
|
209
223
|
*/
|
|
210
224
|
deviceSelection?: EnyoOnboardingV2DeviceSelection;
|
|
211
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* A fixed URL the installer opens or copies — a vendor portal, a manual, a
|
|
228
|
+
* firmware download.
|
|
229
|
+
*
|
|
230
|
+
* Passive content: it produces no routing handle, so a step whose only
|
|
231
|
+
* non-content block is a link still routes through its single `continue`
|
|
232
|
+
* handle. Unlike {@link EnyoOnboardingV2DynamicBlock}, the URL is the same for
|
|
233
|
+
* every installer — nothing is resolved per device.
|
|
234
|
+
*/
|
|
235
|
+
export interface EnyoOnboardingV2LinkBlock extends EnyoOnboardingV2BlockBase {
|
|
236
|
+
type: EnyoOnboardingV2BlockType.Link;
|
|
237
|
+
/**
|
|
238
|
+
* Absolute `http(s)` URL, not translated. Any other scheme is rejected by
|
|
239
|
+
* the validator and by the host: the installer app renders this as a tap
|
|
240
|
+
* target.
|
|
241
|
+
*/
|
|
242
|
+
url: string;
|
|
243
|
+
/** Translated link text (de/en). Falls back to the raw URL when empty. */
|
|
244
|
+
label: EnyoOnboardingTranslatedContent[];
|
|
245
|
+
/** Optional translated context line below the link (de/en). */
|
|
246
|
+
description?: EnyoOnboardingTranslatedContent[];
|
|
247
|
+
/**
|
|
248
|
+
* Show a copy-to-clipboard button. Defaults to `true` — on a phone it is the
|
|
249
|
+
* more useful affordance of the two.
|
|
250
|
+
*/
|
|
251
|
+
copyable?: boolean;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* A possible verdict of an {@link EnyoOnboardingV2InputBlock}'s check; each
|
|
255
|
+
* outcome is a routing handle.
|
|
256
|
+
*/
|
|
257
|
+
export interface EnyoOnboardingV2InputOutcome {
|
|
258
|
+
/** Stable id, unique within the block; referenced by a transition. */
|
|
259
|
+
id: string;
|
|
260
|
+
/**
|
|
261
|
+
* Semantic key, not translated. Either an {@link EnyoDeviceTestOutcomeEnum}
|
|
262
|
+
* member (routed exactly) or one of the binary keys the host collapses
|
|
263
|
+
* onto — `reachable` / `unreachable`. See {@link EnyoOnboardingV2InputBlock}
|
|
264
|
+
* for the full mapping.
|
|
265
|
+
*/
|
|
266
|
+
value: string;
|
|
267
|
+
/** Translated display label for the verdict (de/en). */
|
|
268
|
+
label: EnyoOnboardingTranslatedContent[];
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* The installer types a value and the **host** checks it, producing the branch —
|
|
272
|
+
* unlike {@link EnyoOnboardingV2ChoiceBlock}, where the installer picks the
|
|
273
|
+
* branch directly.
|
|
274
|
+
*
|
|
275
|
+
* For {@link EnyoOnboardingV2InputValueType.IpAddress} the host pairs the typed
|
|
276
|
+
* address with this energy app (creating the network device when the scan never
|
|
277
|
+
* discovered it) and runs the app's registered {@link EnyoDeviceTestHandler}
|
|
278
|
+
* against it, so the verdict is the app's own — it confirms *your* device, not
|
|
279
|
+
* merely a live host. `Text` and `Number` are recorded and take the positive
|
|
280
|
+
* branch; there is nothing the host could check about them.
|
|
281
|
+
*
|
|
282
|
+
* Each outcome MUST have exactly one outgoing transition.
|
|
283
|
+
*
|
|
284
|
+
* **How a verdict picks an outcome**
|
|
285
|
+
*
|
|
286
|
+
* 1. *Exact match wins.* An outcome whose `value` is an
|
|
287
|
+
* {@link EnyoDeviceTestOutcomeEnum} member receives that verdict verbatim —
|
|
288
|
+
* so a guide can route `authentication-required` to a credentials step and
|
|
289
|
+
* `user-action-required` to a "press the pairing button" step.
|
|
290
|
+
* 2. *Otherwise it collapses to a binary pair.* `appliances-created`,
|
|
291
|
+
* `appliances-already-existed` and `device-confirmed-no-appliance` go to the
|
|
292
|
+
* first outcome valued `reachable` / `success` / `found`; everything else
|
|
293
|
+
* (`unreachable`, `not-supported`, `authentication-required`,
|
|
294
|
+
* `access-not-granted`, `user-action-required`, `failed`) goes to the first
|
|
295
|
+
* outcome valued `unreachable` / `failure` / `failed` / `not-found`.
|
|
296
|
+
* 3. `Text` and `Number` run no check: the value is recorded and the flow takes
|
|
297
|
+
* the positive outcome (first `reachable`/`success`/`found`, else the first
|
|
298
|
+
* outcome). Any further outcome on such a block can never fire.
|
|
299
|
+
* 4. An offline hub, a package that is not installed, or no energy app linked to
|
|
300
|
+
* the vendor/model all resolve to the **negative** branch — never an error.
|
|
301
|
+
* The guide always gets an outcome to route on.
|
|
302
|
+
*
|
|
303
|
+
* The typed value is persisted per block in the run state and handed back on
|
|
304
|
+
* resume/back, so a typo is corrected rather than retyped. Values may be secrets
|
|
305
|
+
* (a device password typed into a `Text` input): the host logs outcome keys
|
|
306
|
+
* only, never the value itself.
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```ts
|
|
310
|
+
* onboardingV2Block.input(
|
|
311
|
+
* 'b3',
|
|
312
|
+
* EnyoOnboardingV2InputValueType.IpAddress,
|
|
313
|
+
* t('IP-Adresse des Geräts', 'Device IP address'),
|
|
314
|
+
* t('Gerät prüfen', 'Check device'),
|
|
315
|
+
* [
|
|
316
|
+
* {id: 'ok', value: 'reachable', label: t('Gerät erreichbar', 'Device reachable')},
|
|
317
|
+
* {
|
|
318
|
+
* id: 'auth',
|
|
319
|
+
* value: EnyoDeviceTestOutcomeEnum.AuthenticationRequired,
|
|
320
|
+
* label: t('Passwort nötig', 'Password required'),
|
|
321
|
+
* },
|
|
322
|
+
* {id: 'no', value: 'unreachable', label: t('Nicht erreichbar', 'Not reachable')},
|
|
323
|
+
* ],
|
|
324
|
+
* {placeholder: t('z. B. 192.168.1.42', 'e.g. 192.168.1.42')},
|
|
325
|
+
* );
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
export interface EnyoOnboardingV2InputBlock extends EnyoOnboardingV2BlockBase {
|
|
329
|
+
type: EnyoOnboardingV2BlockType.Input;
|
|
330
|
+
/** What is asked for; drives keyboard, format check and seeded texts. */
|
|
331
|
+
valueType: EnyoOnboardingV2InputValueType;
|
|
332
|
+
/** Translated field label, e.g. "IP-Adresse des Geräts" (de/en). */
|
|
333
|
+
label: EnyoOnboardingTranslatedContent[];
|
|
334
|
+
/** Optional translated placeholder, e.g. "z. B. 192.168.1.42" (de/en). */
|
|
335
|
+
placeholder?: EnyoOnboardingTranslatedContent[];
|
|
336
|
+
/** Optional translated help text — where the installer finds the value (de/en). */
|
|
337
|
+
help?: EnyoOnboardingTranslatedContent[];
|
|
338
|
+
/** Translated submit button text, e.g. "Gerät prüfen" (de/en). */
|
|
339
|
+
submitLabel: EnyoOnboardingTranslatedContent[];
|
|
340
|
+
/** The possible verdicts; each is a routing handle. At least 2. */
|
|
341
|
+
outcomes: EnyoOnboardingV2InputOutcome[];
|
|
342
|
+
}
|
|
212
343
|
/** Any block that can appear in a step's `blocks`. */
|
|
213
|
-
export type EnyoOnboardingV2Block = EnyoOnboardingV2TextBlock | EnyoOnboardingV2HeadlineBlock | EnyoOnboardingV2BulletsBlock | EnyoOnboardingV2ImageBlock | EnyoOnboardingV2HintBlock | EnyoOnboardingV2DynamicBlock | EnyoOnboardingV2ChoiceBlock | EnyoOnboardingV2ActionBlock;
|
|
214
|
-
/**
|
|
215
|
-
|
|
344
|
+
export type EnyoOnboardingV2Block = EnyoOnboardingV2TextBlock | EnyoOnboardingV2HeadlineBlock | EnyoOnboardingV2BulletsBlock | EnyoOnboardingV2ImageBlock | EnyoOnboardingV2HintBlock | EnyoOnboardingV2DynamicBlock | EnyoOnboardingV2ChoiceBlock | EnyoOnboardingV2ActionBlock | EnyoOnboardingV2LinkBlock | EnyoOnboardingV2InputBlock;
|
|
345
|
+
/**
|
|
346
|
+
* Blocks that produce routing handles (a step's decision points).
|
|
347
|
+
*
|
|
348
|
+
* A {@link EnyoOnboardingV2LinkBlock} is deliberately absent: it is passive
|
|
349
|
+
* content and routes nothing.
|
|
350
|
+
*/
|
|
351
|
+
export type EnyoOnboardingV2InteractiveBlock = EnyoOnboardingV2ChoiceBlock | EnyoOnboardingV2ActionBlock | EnyoOnboardingV2InputBlock;
|
|
216
352
|
/** Discriminator for the {@link EnyoOnboardingV2TransitionSource} union. */
|
|
217
353
|
export declare enum EnyoOnboardingV2TransitionSourceKind {
|
|
218
354
|
/** The step's plain "continue" button (steps with no interactive block). */
|
|
219
355
|
Continue = "continue",
|
|
220
356
|
/** A specific option of a `Choice` block was picked. */
|
|
221
357
|
Choice = "choice",
|
|
222
|
-
/** A specific outcome of an `Action` block fired. */
|
|
358
|
+
/** A specific outcome of an `Action` or `Input` block fired. */
|
|
223
359
|
Outcome = "outcome"
|
|
224
360
|
}
|
|
225
361
|
/** Where a transition leaves from within a step. */
|
|
@@ -234,7 +370,7 @@ export type EnyoOnboardingV2TransitionSource =
|
|
|
234
370
|
blockId: string;
|
|
235
371
|
optionId: string;
|
|
236
372
|
}
|
|
237
|
-
/** A specific outcome of an `Action` block fired. */
|
|
373
|
+
/** A specific outcome of an `Action` or `Input` block fired. */
|
|
238
374
|
| {
|
|
239
375
|
kind: EnyoOnboardingV2TransitionSourceKind.Outcome;
|
|
240
376
|
blockId: string;
|
|
@@ -85,6 +85,19 @@ export var EnyoOnboardingV2ActionKind;
|
|
|
85
85
|
*/
|
|
86
86
|
EnyoOnboardingV2ActionKind["DeviceTest"] = "device-test";
|
|
87
87
|
})(EnyoOnboardingV2ActionKind || (EnyoOnboardingV2ActionKind = {}));
|
|
88
|
+
/**
|
|
89
|
+
* What an {@link EnyoOnboardingV2InputBlock} asks the installer for. Drives the
|
|
90
|
+
* keyboard the app shows, the format check it applies and the seeded texts.
|
|
91
|
+
*/
|
|
92
|
+
export var EnyoOnboardingV2InputValueType;
|
|
93
|
+
(function (EnyoOnboardingV2InputValueType) {
|
|
94
|
+
/** Free text; any non-empty value is accepted. */
|
|
95
|
+
EnyoOnboardingV2InputValueType["Text"] = "text";
|
|
96
|
+
/** An IPv4 address. This is the only type the host actually checks. */
|
|
97
|
+
EnyoOnboardingV2InputValueType["IpAddress"] = "ip-address";
|
|
98
|
+
/** A number; `,` and `.` are both accepted as the decimal separator. */
|
|
99
|
+
EnyoOnboardingV2InputValueType["Number"] = "number";
|
|
100
|
+
})(EnyoOnboardingV2InputValueType || (EnyoOnboardingV2InputValueType = {}));
|
|
88
101
|
/**
|
|
89
102
|
* Which devices a {@link EnyoOnboardingV2ActionKind.DeviceTest} block hands to
|
|
90
103
|
* the app.
|
|
@@ -126,6 +139,8 @@ export var EnyoOnboardingV2BlockType;
|
|
|
126
139
|
EnyoOnboardingV2BlockType["Dynamic"] = "dynamic";
|
|
127
140
|
EnyoOnboardingV2BlockType["Choice"] = "choice";
|
|
128
141
|
EnyoOnboardingV2BlockType["Action"] = "action";
|
|
142
|
+
EnyoOnboardingV2BlockType["Link"] = "link";
|
|
143
|
+
EnyoOnboardingV2BlockType["Input"] = "input";
|
|
129
144
|
})(EnyoOnboardingV2BlockType || (EnyoOnboardingV2BlockType = {}));
|
|
130
145
|
// ---------------------------------------------------------------------------
|
|
131
146
|
// Routing: transitions & targets
|
|
@@ -137,7 +152,7 @@ export var EnyoOnboardingV2TransitionSourceKind;
|
|
|
137
152
|
EnyoOnboardingV2TransitionSourceKind["Continue"] = "continue";
|
|
138
153
|
/** A specific option of a `Choice` block was picked. */
|
|
139
154
|
EnyoOnboardingV2TransitionSourceKind["Choice"] = "choice";
|
|
140
|
-
/** A specific outcome of an `Action` block fired. */
|
|
155
|
+
/** A specific outcome of an `Action` or `Input` block fired. */
|
|
141
156
|
EnyoOnboardingV2TransitionSourceKind["Outcome"] = "outcome";
|
|
142
157
|
})(EnyoOnboardingV2TransitionSourceKind || (EnyoOnboardingV2TransitionSourceKind = {}));
|
|
143
158
|
/** Discriminator for the {@link EnyoOnboardingV2Target} union. */
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED