@enyo-energy/energy-app-sdk 1.6.0 → 1.7.0
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 +90 -0
- package/dist/cjs/implementations/onboarding-v2/define-onboarding-guide-v2.d.cts +78 -1
- package/dist/cjs/implementations/onboarding-v2/onboarding-v2-validators.cjs +119 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/packages/energy-app-interval.d.cts +1 -1
- package/dist/cjs/packages/energy-app-onboarding-v2.d.cts +54 -0
- package/dist/cjs/types/enyo-onboarding-v2-device-select.cjs +29 -0
- package/dist/cjs/types/enyo-onboarding-v2-device-select.d.cts +105 -0
- package/dist/cjs/types/enyo-onboarding-v2-validation.cjs +29 -0
- package/dist/cjs/types/enyo-onboarding-v2-validation.d.cts +108 -0
- package/dist/cjs/types/enyo-onboarding-v2.cjs +66 -1
- package/dist/cjs/types/enyo-onboarding-v2.d.cts +206 -8
- package/dist/cjs/types/enyo-onboarding.cjs +5 -3
- package/dist/cjs/types/enyo-onboarding.d.cts +5 -3
- 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 +78 -1
- package/dist/implementations/onboarding-v2/define-onboarding-guide-v2.js +90 -0
- package/dist/implementations/onboarding-v2/onboarding-v2-validators.js +120 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/packages/energy-app-interval.d.ts +1 -1
- package/dist/packages/energy-app-onboarding-v2.d.ts +54 -0
- package/dist/types/enyo-onboarding-v2-device-select.d.ts +105 -0
- package/dist/types/enyo-onboarding-v2-device-select.js +28 -0
- package/dist/types/enyo-onboarding-v2-validation.d.ts +108 -0
- package/dist/types/enyo-onboarding-v2-validation.js +28 -0
- package/dist/types/enyo-onboarding-v2.d.ts +206 -8
- package/dist/types/enyo-onboarding-v2.js +65 -0
- package/dist/types/enyo-onboarding.d.ts +5 -3
- package/dist/types/enyo-onboarding.js +5 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -67,6 +67,62 @@ exports.onboardingV2Block = {
|
|
|
67
67
|
* @param items - One translated entry per bullet (de/en).
|
|
68
68
|
*/
|
|
69
69
|
bullets: (id, items) => ({ id, type: enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Bullets, items }),
|
|
70
|
+
/**
|
|
71
|
+
* A select block — a dropdown whose answer is recorded, not branched on.
|
|
72
|
+
*
|
|
73
|
+
* Use this, not {@link block.choice}, when the answer is data rather than a
|
|
74
|
+
* route: a choice needs one outgoing transition per option, so forty models
|
|
75
|
+
* would mean forty transitions to the same step.
|
|
76
|
+
*
|
|
77
|
+
* @param id - Stable block id, unique within the guide.
|
|
78
|
+
* @param label - Translated field label (de/en).
|
|
79
|
+
* @param options - The selectable options; unique values, at least one.
|
|
80
|
+
* @param opts - Optional `defaultValue`, `help` and `required`.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* block.select('model', t('Modell', 'Model'), [
|
|
85
|
+
* {value: 'sb-3-0', label: t('Sunny Boy 3.0', 'Sunny Boy 3.0')},
|
|
86
|
+
* {value: 'sb-5-0', label: t('Sunny Boy 5.0', 'Sunny Boy 5.0')},
|
|
87
|
+
* ], {required: true})
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
select: (id, label, options, opts) => ({
|
|
91
|
+
id,
|
|
92
|
+
type: enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Select,
|
|
93
|
+
label,
|
|
94
|
+
options,
|
|
95
|
+
...(opts?.defaultValue !== undefined ? { defaultValue: opts.defaultValue } : {}),
|
|
96
|
+
...(opts?.help ? { help: opts.help } : {}),
|
|
97
|
+
...(opts?.required !== undefined ? { required: opts.required } : {}),
|
|
98
|
+
}),
|
|
99
|
+
/**
|
|
100
|
+
* A credentials block — label/value pairs the installer transcribes into the
|
|
101
|
+
* device or a vendor portal.
|
|
102
|
+
*
|
|
103
|
+
* Passive content: it routes nothing, so a step whose only non-content block
|
|
104
|
+
* is this one still leaves through its single `continue` handle.
|
|
105
|
+
*
|
|
106
|
+
* @param id - Stable block id, unique within the guide.
|
|
107
|
+
* @param credentials - The label/value pairs. At least one.
|
|
108
|
+
* @param options - Optional heading, note, and copy-button toggle.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* block.credentials('creds', [
|
|
113
|
+
* {label: t('Benutzername', 'Username'), value: 'enyo'},
|
|
114
|
+
* {label: t('Passwort', 'Password'), value: generated, secret: true},
|
|
115
|
+
* ], {title: t('Zugangsdaten', 'Credentials')})
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
credentials: (id, credentials, options) => ({
|
|
119
|
+
id,
|
|
120
|
+
type: enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Credentials,
|
|
121
|
+
credentials,
|
|
122
|
+
copyable: options?.copyable ?? true,
|
|
123
|
+
...(options?.title ? { title: options.title } : {}),
|
|
124
|
+
...(options?.description ? { description: options.description } : {}),
|
|
125
|
+
}),
|
|
70
126
|
/**
|
|
71
127
|
* An image block addressing an externally hosted image by URL.
|
|
72
128
|
*
|
|
@@ -217,6 +273,40 @@ exports.onboardingV2Block = {
|
|
|
217
273
|
label,
|
|
218
274
|
outcomes,
|
|
219
275
|
}),
|
|
276
|
+
/**
|
|
277
|
+
* A device-select block: the installer picks the device being onboarded from
|
|
278
|
+
* everything the run has found.
|
|
279
|
+
*
|
|
280
|
+
* Use it whenever a scan can turn up more than one candidate. A
|
|
281
|
+
* {@link block.networkScan} branches on found/not-found but binds nothing,
|
|
282
|
+
* so without this the run does not know *which* device it is working on —
|
|
283
|
+
* and {@link EnyoOnboardingV2DeviceSelection.Current} and
|
|
284
|
+
* {@link EnyoOnboardingV2DynamicKind.DeviceIp} have nothing to resolve
|
|
285
|
+
* against.
|
|
286
|
+
*
|
|
287
|
+
* The picker renders what discovery found, so the guide must have scanned:
|
|
288
|
+
* keep {@link EnyoOnboardingV2Guide.requiresNetworkScan} at its default, or
|
|
289
|
+
* place a {@link block.networkScan} ahead of this one.
|
|
290
|
+
*
|
|
291
|
+
* Outcome `value`s must be {@link EnyoOnboardingV2DeviceSelectOutcome}
|
|
292
|
+
* members; route `not-found` to troubleshooting rather than to a step that
|
|
293
|
+
* assumes a device exists.
|
|
294
|
+
*
|
|
295
|
+
* Register an {@link EnyoOnboardingV2DeviceSelectHandler} to turn the pick
|
|
296
|
+
* into appliances — the host awaits it and binds the run to the ids it
|
|
297
|
+
* returns. Without one the pick binds an address and nothing more.
|
|
298
|
+
*
|
|
299
|
+
* @param id - Stable block id, unique within the guide.
|
|
300
|
+
* @param label - Translated trigger button text (de/en).
|
|
301
|
+
* @param outcomes - The `selected` / `not-found` results; each is a routing handle.
|
|
302
|
+
*/
|
|
303
|
+
deviceSelect: (id, label, outcomes) => ({
|
|
304
|
+
id,
|
|
305
|
+
type: enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Action,
|
|
306
|
+
action: enyo_onboarding_v2_js_1.EnyoOnboardingV2ActionKind.DeviceSelect,
|
|
307
|
+
label,
|
|
308
|
+
outcomes,
|
|
309
|
+
}),
|
|
220
310
|
/**
|
|
221
311
|
* An auth block: the installer signs into the energy app's own account
|
|
222
312
|
* system (OAuth / vendor portal).
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* before publishing.
|
|
11
11
|
*/
|
|
12
12
|
import type { EnyoOnboardingTranslatedContent } from '../../types/enyo-onboarding.cjs';
|
|
13
|
-
import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2ChoiceLayout, EnyoOnboardingV2DeviceSelection, EnyoOnboardingV2PauseReason } from '../../types/enyo-onboarding-v2.cjs';
|
|
13
|
+
import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2Credential, EnyoOnboardingV2SelectOption, EnyoOnboardingV2ChoiceLayout, EnyoOnboardingV2DeviceSelection, EnyoOnboardingV2PauseReason } from '../../types/enyo-onboarding-v2.cjs';
|
|
14
14
|
import type { EnyoOnboardingV2ActionOutcome, EnyoOnboardingV2AuthOutcome, EnyoOnboardingV2SetupField, EnyoOnboardingV2SetupOutcome, EnyoOnboardingV2SetupSkipHandle, EnyoOnboardingV2Block, EnyoOnboardingV2ChoiceOption, EnyoOnboardingV2DynamicKind, EnyoOnboardingV2Guide, EnyoOnboardingV2HintVariant, EnyoOnboardingV2InputOutcome, EnyoOnboardingV2InputValueType, 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
|
|
@@ -45,6 +45,55 @@ export declare const onboardingV2Block: {
|
|
|
45
45
|
* @param items - One translated entry per bullet (de/en).
|
|
46
46
|
*/
|
|
47
47
|
bullets: (id: string, items: EnyoOnboardingTranslatedContent[][]) => EnyoOnboardingV2Block;
|
|
48
|
+
/**
|
|
49
|
+
* A select block — a dropdown whose answer is recorded, not branched on.
|
|
50
|
+
*
|
|
51
|
+
* Use this, not {@link block.choice}, when the answer is data rather than a
|
|
52
|
+
* route: a choice needs one outgoing transition per option, so forty models
|
|
53
|
+
* would mean forty transitions to the same step.
|
|
54
|
+
*
|
|
55
|
+
* @param id - Stable block id, unique within the guide.
|
|
56
|
+
* @param label - Translated field label (de/en).
|
|
57
|
+
* @param options - The selectable options; unique values, at least one.
|
|
58
|
+
* @param opts - Optional `defaultValue`, `help` and `required`.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* block.select('model', t('Modell', 'Model'), [
|
|
63
|
+
* {value: 'sb-3-0', label: t('Sunny Boy 3.0', 'Sunny Boy 3.0')},
|
|
64
|
+
* {value: 'sb-5-0', label: t('Sunny Boy 5.0', 'Sunny Boy 5.0')},
|
|
65
|
+
* ], {required: true})
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
select: (id: string, label: EnyoOnboardingTranslatedContent[], options: EnyoOnboardingV2SelectOption[], opts?: {
|
|
69
|
+
defaultValue?: string;
|
|
70
|
+
help?: EnyoOnboardingTranslatedContent[];
|
|
71
|
+
required?: boolean;
|
|
72
|
+
}) => EnyoOnboardingV2Block;
|
|
73
|
+
/**
|
|
74
|
+
* A credentials block — label/value pairs the installer transcribes into the
|
|
75
|
+
* device or a vendor portal.
|
|
76
|
+
*
|
|
77
|
+
* Passive content: it routes nothing, so a step whose only non-content block
|
|
78
|
+
* is this one still leaves through its single `continue` handle.
|
|
79
|
+
*
|
|
80
|
+
* @param id - Stable block id, unique within the guide.
|
|
81
|
+
* @param credentials - The label/value pairs. At least one.
|
|
82
|
+
* @param options - Optional heading, note, and copy-button toggle.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* block.credentials('creds', [
|
|
87
|
+
* {label: t('Benutzername', 'Username'), value: 'enyo'},
|
|
88
|
+
* {label: t('Passwort', 'Password'), value: generated, secret: true},
|
|
89
|
+
* ], {title: t('Zugangsdaten', 'Credentials')})
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
credentials: (id: string, credentials: EnyoOnboardingV2Credential[], options?: {
|
|
93
|
+
title?: EnyoOnboardingTranslatedContent[];
|
|
94
|
+
description?: EnyoOnboardingTranslatedContent[];
|
|
95
|
+
copyable?: boolean;
|
|
96
|
+
}) => EnyoOnboardingV2Block;
|
|
48
97
|
/**
|
|
49
98
|
* An image block addressing an externally hosted image by URL.
|
|
50
99
|
*
|
|
@@ -169,6 +218,34 @@ export declare const onboardingV2Block: {
|
|
|
169
218
|
* @param outcomes - The `paired` / `not-found` / `failure` results; each is a routing handle.
|
|
170
219
|
*/
|
|
171
220
|
eebusPair: (id: string, label: EnyoOnboardingTranslatedContent[], outcomes: EnyoOnboardingV2ActionOutcome[]) => EnyoOnboardingV2Block;
|
|
221
|
+
/**
|
|
222
|
+
* A device-select block: the installer picks the device being onboarded from
|
|
223
|
+
* everything the run has found.
|
|
224
|
+
*
|
|
225
|
+
* Use it whenever a scan can turn up more than one candidate. A
|
|
226
|
+
* {@link block.networkScan} branches on found/not-found but binds nothing,
|
|
227
|
+
* so without this the run does not know *which* device it is working on —
|
|
228
|
+
* and {@link EnyoOnboardingV2DeviceSelection.Current} and
|
|
229
|
+
* {@link EnyoOnboardingV2DynamicKind.DeviceIp} have nothing to resolve
|
|
230
|
+
* against.
|
|
231
|
+
*
|
|
232
|
+
* The picker renders what discovery found, so the guide must have scanned:
|
|
233
|
+
* keep {@link EnyoOnboardingV2Guide.requiresNetworkScan} at its default, or
|
|
234
|
+
* place a {@link block.networkScan} ahead of this one.
|
|
235
|
+
*
|
|
236
|
+
* Outcome `value`s must be {@link EnyoOnboardingV2DeviceSelectOutcome}
|
|
237
|
+
* members; route `not-found` to troubleshooting rather than to a step that
|
|
238
|
+
* assumes a device exists.
|
|
239
|
+
*
|
|
240
|
+
* Register an {@link EnyoOnboardingV2DeviceSelectHandler} to turn the pick
|
|
241
|
+
* into appliances — the host awaits it and binds the run to the ids it
|
|
242
|
+
* returns. Without one the pick binds an address and nothing more.
|
|
243
|
+
*
|
|
244
|
+
* @param id - Stable block id, unique within the guide.
|
|
245
|
+
* @param label - Translated trigger button text (de/en).
|
|
246
|
+
* @param outcomes - The `selected` / `not-found` results; each is a routing handle.
|
|
247
|
+
*/
|
|
248
|
+
deviceSelect: (id: string, label: EnyoOnboardingTranslatedContent[], outcomes: EnyoOnboardingV2ActionOutcome[]) => EnyoOnboardingV2Block;
|
|
172
249
|
/**
|
|
173
250
|
* An auth block: the installer signs into the energy app's own account
|
|
174
251
|
* system (OAuth / vendor portal).
|
|
@@ -83,6 +83,8 @@ function validateOnboardingGuideV2(guide, context) {
|
|
|
83
83
|
warnings.push(`${at}: no content blocks.`);
|
|
84
84
|
validateActionBlocks(step, at, errors, warnings);
|
|
85
85
|
validateLinkBlocks(step, at, errors, warnings);
|
|
86
|
+
validateCredentialsBlocks(step, at, errors, warnings);
|
|
87
|
+
validateSelectBlocks(step, at, errors, warnings);
|
|
86
88
|
validateImageBlocks(step, at, errors, warnings, context);
|
|
87
89
|
validateInputBlocks(step, at, errors, warnings);
|
|
88
90
|
validateAuthBlocks(step, at, errors, warnings);
|
|
@@ -191,6 +193,9 @@ function validateActionBlocks(step, at, errors, warnings) {
|
|
|
191
193
|
if (block.action === enyo_onboarding_v2_js_1.EnyoOnboardingV2ActionKind.OcppConnect) {
|
|
192
194
|
validateOcppConnectOutcomes(block, at, errors);
|
|
193
195
|
}
|
|
196
|
+
if (block.action === enyo_onboarding_v2_js_1.EnyoOnboardingV2ActionKind.DeviceSelect) {
|
|
197
|
+
validateDeviceSelectOutcomes(block, at, errors, warnings);
|
|
198
|
+
}
|
|
194
199
|
if (block.action === enyo_onboarding_v2_js_1.EnyoOnboardingV2ActionKind.EebusPair) {
|
|
195
200
|
validateEebusPairOutcomes(block, at, errors, warnings);
|
|
196
201
|
}
|
|
@@ -247,6 +252,76 @@ function validateLinkBlocks(step, at, errors, warnings) {
|
|
|
247
252
|
}
|
|
248
253
|
}
|
|
249
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Validates the select blocks of a step.
|
|
257
|
+
*
|
|
258
|
+
* A select records an answer rather than routing on one, so the failures that
|
|
259
|
+
* matter are the ones that make the recorded value ambiguous or unreachable:
|
|
260
|
+
* duplicate option values (the app cannot tell which was picked) and a
|
|
261
|
+
* `defaultValue` naming no option (nothing is pre-selected, silently).
|
|
262
|
+
*/
|
|
263
|
+
function validateSelectBlocks(step, at, errors, warnings) {
|
|
264
|
+
for (const block of step.blocks ?? []) {
|
|
265
|
+
if (block.type !== enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Select)
|
|
266
|
+
continue;
|
|
267
|
+
if (!block.label?.length) {
|
|
268
|
+
warnings.push(`${at}: select block "${block.id}" has no label.`);
|
|
269
|
+
}
|
|
270
|
+
if (!block.options?.length) {
|
|
271
|
+
errors.push(`${at}: select block "${block.id}" has no options.`);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (block.options.length === 1) {
|
|
275
|
+
warnings.push(`${at}: select block "${block.id}" offers a single option — ` +
|
|
276
|
+
`there is nothing for the installer to choose.`);
|
|
277
|
+
}
|
|
278
|
+
const seen = new Set();
|
|
279
|
+
block.options.forEach((option, index) => {
|
|
280
|
+
if (!option.value?.trim()) {
|
|
281
|
+
errors.push(`${at}: select block "${block.id}" option ${index} has no value.`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (seen.has(option.value)) {
|
|
285
|
+
errors.push(`${at}: select block "${block.id}" uses the option value ` +
|
|
286
|
+
`"${option.value}" more than once.`);
|
|
287
|
+
}
|
|
288
|
+
seen.add(option.value);
|
|
289
|
+
if (!option.label?.length) {
|
|
290
|
+
warnings.push(`${at}: select block "${block.id}" option "${option.value}" has no label.`);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
if (block.defaultValue !== undefined && !seen.has(block.defaultValue)) {
|
|
294
|
+
errors.push(`${at}: select block "${block.id}" defaultValue "${block.defaultValue}" ` +
|
|
295
|
+
`matches no option.`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Validates the credentials blocks of a step.
|
|
301
|
+
*
|
|
302
|
+
* A pair with no value is the failure that matters: it renders as an empty row,
|
|
303
|
+
* and the installer has nothing to type into the device. An untranslated label
|
|
304
|
+
* only degrades the display, so it is a warning.
|
|
305
|
+
*/
|
|
306
|
+
function validateCredentialsBlocks(step, at, errors, warnings) {
|
|
307
|
+
for (const block of step.blocks ?? []) {
|
|
308
|
+
if (block.type !== enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Credentials)
|
|
309
|
+
continue;
|
|
310
|
+
if (!block.credentials?.length) {
|
|
311
|
+
errors.push(`${at}: credentials block "${block.id}" has no credentials.`);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
block.credentials.forEach((credential, index) => {
|
|
315
|
+
if (!credential.value?.trim()) {
|
|
316
|
+
errors.push(`${at}: credentials block "${block.id}" entry ${index} has no value — ` +
|
|
317
|
+
`it would render as an empty row.`);
|
|
318
|
+
}
|
|
319
|
+
if (!credential.label?.length) {
|
|
320
|
+
warnings.push(`${at}: credentials block "${block.id}" entry ${index} has no label.`);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
250
325
|
/** An image-block `file` reference — the slug shape a package file is named with. */
|
|
251
326
|
const FILE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
252
327
|
/**
|
|
@@ -366,6 +441,10 @@ function validateInputBlocks(step, at, errors, warnings) {
|
|
|
366
441
|
if (!block.submitLabel?.length) {
|
|
367
442
|
errors.push(`${at}: input block "${block.id}" has no submitLabel.`);
|
|
368
443
|
}
|
|
444
|
+
if (block.validated && block.valueType === enyo_onboarding_v2_js_1.EnyoOnboardingV2InputValueType.IpAddress) {
|
|
445
|
+
errors.push(`${at}: input block "${block.id}" sets validated on an ip-address input — ` +
|
|
446
|
+
`the device test already produces the app's own verdict.`);
|
|
447
|
+
}
|
|
369
448
|
const outcomes = block.outcomes ?? [];
|
|
370
449
|
if (outcomes.length < 2) {
|
|
371
450
|
errors.push(`${at}: input block "${block.id}" needs at least 2 outcomes.`);
|
|
@@ -432,6 +511,46 @@ function validateOcppConnectOutcomes(block, at, errors) {
|
|
|
432
511
|
}
|
|
433
512
|
}
|
|
434
513
|
}
|
|
514
|
+
/** Every {@link EnyoOnboardingV2DeviceSelectOutcome} value. */
|
|
515
|
+
const DEVICE_SELECT_OUTCOMES = new Set(Object.values(enyo_onboarding_v2_js_1.EnyoOnboardingV2DeviceSelectOutcome));
|
|
516
|
+
/**
|
|
517
|
+
* Validates the outcomes of an {@link EnyoOnboardingV2ActionKind.DeviceSelect}
|
|
518
|
+
* block.
|
|
519
|
+
*
|
|
520
|
+
* The block reports one of two things — the installer picked a device, or the
|
|
521
|
+
* run came away without one — so its outcome `value`s are closed over
|
|
522
|
+
* {@link EnyoOnboardingV2DeviceSelectOutcome}. Anything else is an outcome that
|
|
523
|
+
* can never fire.
|
|
524
|
+
*
|
|
525
|
+
* A missing `selected` branch strands every successful pick, and a missing
|
|
526
|
+
* `not-found` branch strands the installer whose device is not in the list —
|
|
527
|
+
* both warnings rather than errors, so a guide can be staged step by step.
|
|
528
|
+
*
|
|
529
|
+
* @param block - The device-select action block being checked.
|
|
530
|
+
* @param at - Human-readable location prefix for messages.
|
|
531
|
+
* @param errors - Collector for blocking problems.
|
|
532
|
+
* @param warnings - Collector for advisory problems.
|
|
533
|
+
*/
|
|
534
|
+
function validateDeviceSelectOutcomes(block, at, errors, warnings) {
|
|
535
|
+
const values = new Set();
|
|
536
|
+
for (const outcome of block.outcomes ?? []) {
|
|
537
|
+
if (!DEVICE_SELECT_OUTCOMES.has(outcome.value)) {
|
|
538
|
+
errors.push(`${at}: device-select block "${block.id}" has outcome value "${outcome.value}", which is not an EnyoOnboardingV2DeviceSelectOutcome member.`);
|
|
539
|
+
}
|
|
540
|
+
else if (values.has(outcome.value)) {
|
|
541
|
+
errors.push(`${at}: device-select block "${block.id}" wires outcome value "${outcome.value}" more than once.`);
|
|
542
|
+
}
|
|
543
|
+
values.add(outcome.value);
|
|
544
|
+
}
|
|
545
|
+
if (!values.has(enyo_onboarding_v2_js_1.EnyoOnboardingV2DeviceSelectOutcome.Selected)) {
|
|
546
|
+
warnings.push(`${at}: device-select block "${block.id}" has no "${enyo_onboarding_v2_js_1.EnyoOnboardingV2DeviceSelectOutcome.Selected}" outcome — ` +
|
|
547
|
+
'a picked device would have nowhere to go.');
|
|
548
|
+
}
|
|
549
|
+
if (!values.has(enyo_onboarding_v2_js_1.EnyoOnboardingV2DeviceSelectOutcome.NotFound)) {
|
|
550
|
+
warnings.push(`${at}: device-select block "${block.id}" has no "${enyo_onboarding_v2_js_1.EnyoOnboardingV2DeviceSelectOutcome.NotFound}" outcome — ` +
|
|
551
|
+
'an installer whose device is not in the list would be stranded.');
|
|
552
|
+
}
|
|
553
|
+
}
|
|
435
554
|
/** Every {@link EnyoOnboardingV2EebusPairOutcome} value. */
|
|
436
555
|
const EEBUS_PAIR_OUTCOMES = new Set(Object.values(enyo_onboarding_v2_js_1.EnyoOnboardingV2EebusPairOutcome));
|
|
437
556
|
/**
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -103,6 +103,8 @@ __exportStar(require("./packages/energy-app-onboarding-v2.cjs"), exports);
|
|
|
103
103
|
__exportStar(require("./implementations/onboarding-v2/onboarding-v2-provider-validators.cjs"), exports);
|
|
104
104
|
__exportStar(require("./types/enyo-onboarding-v2-dynamic.cjs"), exports);
|
|
105
105
|
__exportStar(require("./types/enyo-onboarding-v2-additional-setup.cjs"), exports);
|
|
106
|
+
__exportStar(require("./types/enyo-onboarding-v2-validation.cjs"), exports);
|
|
107
|
+
__exportStar(require("./types/enyo-onboarding-v2-device-select.cjs"), exports);
|
|
106
108
|
__exportStar(require("./implementations/onboarding-v2/onboarding-v2-dynamic-validators.cjs"), exports);
|
|
107
109
|
__exportStar(require("./implementations/files/define-public-file.cjs"), exports);
|
|
108
110
|
__exportStar(require("./implementations/files/public-file-validators.cjs"), exports);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -87,6 +87,8 @@ export * from './packages/energy-app-onboarding-v2.cjs';
|
|
|
87
87
|
export * from './implementations/onboarding-v2/onboarding-v2-provider-validators.cjs';
|
|
88
88
|
export * from './types/enyo-onboarding-v2-dynamic.cjs';
|
|
89
89
|
export * from './types/enyo-onboarding-v2-additional-setup.cjs';
|
|
90
|
+
export * from './types/enyo-onboarding-v2-validation.cjs';
|
|
91
|
+
export * from './types/enyo-onboarding-v2-device-select.cjs';
|
|
90
92
|
export * from './implementations/onboarding-v2/onboarding-v2-dynamic-validators.cjs';
|
|
91
93
|
export * from './implementations/files/define-public-file.cjs';
|
|
92
94
|
export * from './implementations/files/public-file-validators.cjs';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Supported interval durations for scheduled tasks.
|
|
3
3
|
* Provides predefined time intervals from 1 second to 1 hour.
|
|
4
4
|
*/
|
|
5
|
-
export type IntervalDuration = '1s' | '5s' | '10s' | '30s' | '1m' | '5m' | '1hr';
|
|
5
|
+
export type IntervalDuration = '1s' | '2s' | '3s' | '5s' | '10s' | '30s' | '1m' | '5m' | '1hr';
|
|
6
6
|
/**
|
|
7
7
|
* Interface for managing scheduled intervals in enyo packages.
|
|
8
8
|
* Provides functionality to create and manage recurring tasks.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { EnyoOnboardingV2GuidesRequest, EnyoOnboardingV2GuidesResult } from '../types/enyo-onboarding-v2-provider.cjs';
|
|
2
2
|
import type { EnyoOnboardingV2DynamicRequest, EnyoOnboardingV2DynamicResult } from '../types/enyo-onboarding-v2-dynamic.cjs';
|
|
3
3
|
import type { EnyoOnboardingV2AdditionalSetupRequest, EnyoOnboardingV2AdditionalSetupResult } from '../types/enyo-onboarding-v2-additional-setup.cjs';
|
|
4
|
+
import type { EnyoOnboardingV2ValidationHandler } from '../types/enyo-onboarding-v2-validation.cjs';
|
|
5
|
+
import type { EnyoOnboardingV2DeviceSelectHandler } from '../types/enyo-onboarding-v2-device-select.cjs';
|
|
4
6
|
/**
|
|
5
7
|
* Which run of a named guide an app means.
|
|
6
8
|
*
|
|
@@ -428,4 +430,56 @@ export interface EnergyAppOnboardingV2 {
|
|
|
428
430
|
* @returns Promise that resolves once the handler has been removed.
|
|
429
431
|
*/
|
|
430
432
|
deregisterAdditionalSetupHandler(): Promise<void>;
|
|
433
|
+
/**
|
|
434
|
+
* Registers the handler the host calls to check a value typed into an
|
|
435
|
+
* {@link EnyoOnboardingV2InputBlock} that set
|
|
436
|
+
* {@link EnyoOnboardingV2InputBlock.validated}.
|
|
437
|
+
*
|
|
438
|
+
* One handler serves every validated input across all of this app's guides.
|
|
439
|
+
* Registering again replaces the previous one.
|
|
440
|
+
*
|
|
441
|
+
* A guide that declares `validated` blocks while no handler is registered is
|
|
442
|
+
* not broken — unanswered validation is treated as acceptance, exactly like
|
|
443
|
+
* a timeout — but nothing is checked, so register the handler before
|
|
444
|
+
* publishing guides that rely on it.
|
|
445
|
+
*
|
|
446
|
+
* @param handler - Called with the submitted value; answers valid or invalid
|
|
447
|
+
* @returns Promise that resolves once the handler is registered
|
|
448
|
+
*/
|
|
449
|
+
registerInputValidationHandler(handler: EnyoOnboardingV2ValidationHandler): Promise<void>;
|
|
450
|
+
/**
|
|
451
|
+
* Removes the registered input validation handler. Validated inputs then
|
|
452
|
+
* accept every value, as if no handler had ever been registered.
|
|
453
|
+
*
|
|
454
|
+
* @returns Promise that resolves once the handler is removed
|
|
455
|
+
*/
|
|
456
|
+
deregisterInputValidationHandler(): Promise<void>;
|
|
457
|
+
/**
|
|
458
|
+
* Registers the handler the host calls when an installer picks devices in an
|
|
459
|
+
* {@link EnyoOnboardingV2ActionKind.DeviceSelect} block, so the app can turn
|
|
460
|
+
* them into appliances and hand back the ids.
|
|
461
|
+
*
|
|
462
|
+
* The host awaits the answer and binds the run to the returned appliances,
|
|
463
|
+
* which is what fills `applianceId` for later dynamic and additional-setup
|
|
464
|
+
* requests on the same run.
|
|
465
|
+
*
|
|
466
|
+
* Optional. Without a handler the block still works — the run takes its
|
|
467
|
+
* `selected` branch and stays bound to the picked device — but no appliance
|
|
468
|
+
* is created, so a guide that ends there produces an address and nothing the
|
|
469
|
+
* energy manager can read or control.
|
|
470
|
+
*
|
|
471
|
+
* One handler serves every device-select block across all of this app's
|
|
472
|
+
* guides. Registering again replaces the previous one.
|
|
473
|
+
*
|
|
474
|
+
* @param handler - Called with the picked devices; answers with appliance ids
|
|
475
|
+
* @returns Promise that resolves once the handler is registered
|
|
476
|
+
*/
|
|
477
|
+
registerDeviceSelectHandler(handler: EnyoOnboardingV2DeviceSelectHandler): Promise<void>;
|
|
478
|
+
/**
|
|
479
|
+
* Removes the registered device-select handler. Picks then create no
|
|
480
|
+
* appliances, as if no handler had ever been registered.
|
|
481
|
+
*
|
|
482
|
+
* @returns Promise that resolves once the handler is removed
|
|
483
|
+
*/
|
|
484
|
+
deregisterDeviceSelectHandler(): Promise<void>;
|
|
431
485
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Onboarding guide **v2 device selection** — the host handing an energy app the
|
|
4
|
+
* network devices an installer picked, and taking back the appliances the app
|
|
5
|
+
* made of them.
|
|
6
|
+
*
|
|
7
|
+
* An {@link EnyoOnboardingV2ActionKind.DeviceSelect} block answers "which of
|
|
8
|
+
* these is it?" and binds the run to the installer's pick. That binding is an
|
|
9
|
+
* address, not an appliance: nothing in the system yet represents the device as
|
|
10
|
+
* something the energy manager can read or control. This model closes that gap —
|
|
11
|
+
* the app is asked to turn the picked device(s) into appliances and answer with
|
|
12
|
+
* their ids, and the host binds the run to them so later steps have an
|
|
13
|
+
* `applianceId` to work with.
|
|
14
|
+
*
|
|
15
|
+
* **Distinct from {@link EnyoDeviceTestHandler}, which stays the right tool when
|
|
16
|
+
* the guide must branch.** A device test produces a verdict per device and an
|
|
17
|
+
* aggregate outcome the graph routes on — the app decides whether the device is
|
|
18
|
+
* usable. Here the installer already decided; there is no verdict, no branching,
|
|
19
|
+
* and the handler's only job is to produce appliances. Reach for the device test
|
|
20
|
+
* when "we could not use it" must lead somewhere different in the flow.
|
|
21
|
+
*
|
|
22
|
+
* Registering is optional. Without a handler the block still works: the run
|
|
23
|
+
* takes its `selected` branch and stays bound to the device, and no appliance is
|
|
24
|
+
* created.
|
|
25
|
+
*
|
|
26
|
+
* Pure type declarations (no runtime logic). Register the handler through
|
|
27
|
+
* {@link EnergyAppOnboardingV2} (`../packages/energy-app-onboarding-v2.ts`).
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding guide **v2 device selection** — the host handing an energy app the
|
|
3
|
+
* network devices an installer picked, and taking back the appliances the app
|
|
4
|
+
* made of them.
|
|
5
|
+
*
|
|
6
|
+
* An {@link EnyoOnboardingV2ActionKind.DeviceSelect} block answers "which of
|
|
7
|
+
* these is it?" and binds the run to the installer's pick. That binding is an
|
|
8
|
+
* address, not an appliance: nothing in the system yet represents the device as
|
|
9
|
+
* something the energy manager can read or control. This model closes that gap —
|
|
10
|
+
* the app is asked to turn the picked device(s) into appliances and answer with
|
|
11
|
+
* their ids, and the host binds the run to them so later steps have an
|
|
12
|
+
* `applianceId` to work with.
|
|
13
|
+
*
|
|
14
|
+
* **Distinct from {@link EnyoDeviceTestHandler}, which stays the right tool when
|
|
15
|
+
* the guide must branch.** A device test produces a verdict per device and an
|
|
16
|
+
* aggregate outcome the graph routes on — the app decides whether the device is
|
|
17
|
+
* usable. Here the installer already decided; there is no verdict, no branching,
|
|
18
|
+
* and the handler's only job is to produce appliances. Reach for the device test
|
|
19
|
+
* when "we could not use it" must lead somewhere different in the flow.
|
|
20
|
+
*
|
|
21
|
+
* Registering is optional. Without a handler the block still works: the run
|
|
22
|
+
* takes its `selected` branch and stays bound to the device, and no appliance is
|
|
23
|
+
* created.
|
|
24
|
+
*
|
|
25
|
+
* Pure type declarations (no runtime logic). Register the handler through
|
|
26
|
+
* {@link EnergyAppOnboardingV2} (`../packages/energy-app-onboarding-v2.ts`).
|
|
27
|
+
*/
|
|
28
|
+
import type { EnyoNetworkDevice } from './enyo-network-device.cjs';
|
|
29
|
+
/**
|
|
30
|
+
* One "the installer picked these — make appliances of them" request.
|
|
31
|
+
*
|
|
32
|
+
* Raised when a {@link EnyoOnboardingV2ActionKind.DeviceSelect} block completes,
|
|
33
|
+
* so it is on the critical path of a screen someone is waiting on.
|
|
34
|
+
*/
|
|
35
|
+
export interface EnyoOnboardingV2DeviceSelectRequest {
|
|
36
|
+
/** Correlates this request with its result. Unique per request. */
|
|
37
|
+
requestId: string;
|
|
38
|
+
/** The {@link EnyoOnboardingV2ActionBlock.id} the pick was made on. */
|
|
39
|
+
blockId: string;
|
|
40
|
+
/**
|
|
41
|
+
* The {@link EnyoOnboardingV2Step.name} the block sits on, so an app can tell
|
|
42
|
+
* two picks apart in a guide that selects more than once, and so a log line
|
|
43
|
+
* says where an empty answer came from.
|
|
44
|
+
*/
|
|
45
|
+
stepName: string;
|
|
46
|
+
/**
|
|
47
|
+
* What the installer picked, as detected by the host. Never empty.
|
|
48
|
+
*
|
|
49
|
+
* An array because one pick can legitimately become several appliances — a
|
|
50
|
+
* hybrid inverter is an inverter and a battery — and because a block that
|
|
51
|
+
* later offers a multiple selection delivers here without a breaking change.
|
|
52
|
+
* A single-pick block sends exactly one entry.
|
|
53
|
+
*/
|
|
54
|
+
devices: EnyoNetworkDevice[];
|
|
55
|
+
/** The appliance the run is already bound to, when one exists. */
|
|
56
|
+
applianceId?: string;
|
|
57
|
+
/**
|
|
58
|
+
* The budget for this request, in milliseconds.
|
|
59
|
+
*
|
|
60
|
+
* The host owns the clock: once it is spent the host stops waiting and
|
|
61
|
+
* continues as if no appliance had been produced. The handler is not
|
|
62
|
+
* notified, so it must bound its own work — cap socket timeouts and retries
|
|
63
|
+
* so they fit inside the budget.
|
|
64
|
+
*/
|
|
65
|
+
timeoutMs: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* An app's answer to one {@link EnyoOnboardingV2DeviceSelectRequest}.
|
|
69
|
+
*/
|
|
70
|
+
export interface EnyoOnboardingV2DeviceSelectResult {
|
|
71
|
+
/** The {@link EnyoOnboardingV2DeviceSelectRequest.requestId} this answers. */
|
|
72
|
+
requestId: string;
|
|
73
|
+
/**
|
|
74
|
+
* The appliances the picked device(s) are now represented by — created just
|
|
75
|
+
* now, or already existing and re-confirmed. The host binds the run to them,
|
|
76
|
+
* which is what fills `applianceId` for later dynamic and additional-setup
|
|
77
|
+
* requests.
|
|
78
|
+
*
|
|
79
|
+
* Return the ids of appliances that already existed too. Omitting them
|
|
80
|
+
* because the app did not create them in this call leaves the run unbound to
|
|
81
|
+
* a device it can plainly see.
|
|
82
|
+
*
|
|
83
|
+
* An empty array is a valid answer and means no appliance could be made. It
|
|
84
|
+
* does **not** re-route the flow — the run still takes the block's
|
|
85
|
+
* `selected` branch. A guide that must go somewhere else in that case needs
|
|
86
|
+
* an {@link EnyoOnboardingV2ActionKind.DeviceTest} block, whose verdict is a
|
|
87
|
+
* routing handle.
|
|
88
|
+
*/
|
|
89
|
+
applianceIds: string[];
|
|
90
|
+
/** Untranslated technical detail for support, e.g. why nothing was created. Not shown to users. */
|
|
91
|
+
detail?: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Handles the host's "make appliances of these devices" requests.
|
|
95
|
+
*
|
|
96
|
+
* One handler serves every device-select block the app's guides declare —
|
|
97
|
+
* switch on {@link EnyoOnboardingV2DeviceSelectRequest.blockId} (and `stepName`
|
|
98
|
+
* where a block id repeats across guides) when the treatment differs.
|
|
99
|
+
*
|
|
100
|
+
* Throwing is treated the same as answering with no appliance ids: the run
|
|
101
|
+
* continues on the block's `selected` branch, unbound. Creating an appliance is
|
|
102
|
+
* a convenience the flow does not depend on, so a handler must never strand an
|
|
103
|
+
* installer by failing loudly.
|
|
104
|
+
*/
|
|
105
|
+
export type EnyoOnboardingV2DeviceSelectHandler = (request: EnyoOnboardingV2DeviceSelectRequest) => Promise<EnyoOnboardingV2DeviceSelectResult>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Onboarding guide **v2 input validation** — the host asking an energy app
|
|
4
|
+
* whether a typed value is acceptable, before the run moves on.
|
|
5
|
+
*
|
|
6
|
+
* An {@link EnyoOnboardingV2InputBlock} of type
|
|
7
|
+
* {@link EnyoOnboardingV2InputValueType.IpAddress} already gets the app's own
|
|
8
|
+
* verdict: the host runs the registered {@link EnyoDeviceTestHandler} and routes
|
|
9
|
+
* on the outcome. `Text`, `Number` and `Password` have no such check — the value
|
|
10
|
+
* is recorded and the flow takes the positive outcome, whatever was typed. A
|
|
11
|
+
* mistyped serial number or a wrong API token is therefore accepted at the step
|
|
12
|
+
* that asked for it and only fails later, somewhere with no installer standing
|
|
13
|
+
* in front of it.
|
|
14
|
+
*
|
|
15
|
+
* This model closes that gap. An input block that opts in
|
|
16
|
+
* ({@link EnyoOnboardingV2InputBlock.validated}) has its value handed to the
|
|
17
|
+
* app, which answers valid or invalid — with a translated message the installer
|
|
18
|
+
* reads on the spot.
|
|
19
|
+
*
|
|
20
|
+
* **Validation is not a branch.** A rejected value keeps the installer on the
|
|
21
|
+
* step with the message shown; it produces no outcome and needs no transition,
|
|
22
|
+
* so adding `validated` to a block never changes a guide's graph. Use an
|
|
23
|
+
* outcome when the flow should go somewhere different, and validation when the
|
|
24
|
+
* answer is simply wrong.
|
|
25
|
+
*
|
|
26
|
+
* Pure type declarations (no runtime logic). Register the handler through
|
|
27
|
+
* {@link EnergyAppOnboardingV2} (`../packages/energy-app-onboarding-v2.ts`).
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|