@enyo-energy/energy-app-sdk 1.1.0 → 1.2.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/onboarding-v2-provider-validators.cjs +20 -2
- package/dist/cjs/implementations/onboarding-v2/onboarding-v2-validators.cjs +32 -0
- package/dist/cjs/packages/energy-app-onboarding-v2.d.cts +140 -0
- package/dist/cjs/types/enyo-onboarding-v2.d.cts +29 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/implementations/onboarding-v2/onboarding-v2-provider-validators.js +20 -2
- package/dist/implementations/onboarding-v2/onboarding-v2-validators.js +32 -0
- package/dist/packages/energy-app-onboarding-v2.d.ts +140 -0
- package/dist/types/enyo-onboarding-v2.d.ts +29 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -47,14 +47,18 @@ const ANY_MODEL = '*';
|
|
|
47
47
|
/**
|
|
48
48
|
* A short human-readable label for a guide, for use in messages.
|
|
49
49
|
*
|
|
50
|
-
* Prefers
|
|
51
|
-
*
|
|
50
|
+
* Prefers {@link EnyoOnboardingV2Guide.name} — it is the handle the author will
|
|
51
|
+
* search for — then the first translated title, then the start variant, so a
|
|
52
|
+
* guide with neither a name nor a title is still identifiable.
|
|
52
53
|
*
|
|
53
54
|
* @param guide - The guide to label.
|
|
54
55
|
* @param index - Its position in the answer's `guides` array.
|
|
55
56
|
* @returns A message prefix such as ``guides[2] ("Wallbox über OCPP")``.
|
|
56
57
|
*/
|
|
57
58
|
function guideLabel(guide, index) {
|
|
59
|
+
const name = guide.name?.trim();
|
|
60
|
+
if (name)
|
|
61
|
+
return `guides[${index}] ("${name}")`;
|
|
58
62
|
const title = guide.title?.[0]?.value;
|
|
59
63
|
return `guides[${index}] (${title ? `"${title}"` : (guide.startVariant ?? '?')})`;
|
|
60
64
|
}
|
|
@@ -122,6 +126,9 @@ function validateOnboardingV2GuidesResult(result, context) {
|
|
|
122
126
|
}
|
|
123
127
|
// Which guide(s) claimed each binding, so a collision can name both sides.
|
|
124
128
|
const claimedBy = new Map();
|
|
129
|
+
// Same, for the explicit `name` handles — a duplicate makes an app unable to
|
|
130
|
+
// say which of the two it means.
|
|
131
|
+
const namedBy = new Map();
|
|
125
132
|
for (const [i, guide] of result.guides.entries()) {
|
|
126
133
|
const at = guideLabel(guide, i);
|
|
127
134
|
const guideResult = (0, onboarding_v2_validators_js_1.validateOnboardingGuideV2)(guide, context);
|
|
@@ -134,6 +141,17 @@ function validateOnboardingV2GuidesResult(result, context) {
|
|
|
134
141
|
else if (!guide.modelIds?.length) {
|
|
135
142
|
warnings.push(`${at}: no modelIds — this guide applies to every model of "${guide.vendorId}".`);
|
|
136
143
|
}
|
|
144
|
+
const name = guide.name?.trim();
|
|
145
|
+
if (name) {
|
|
146
|
+
const previouslyNamed = namedBy.get(name);
|
|
147
|
+
if (previouslyNamed) {
|
|
148
|
+
errors.push(`${at}: name "${name}" is already used by ${previouslyNamed} — ` +
|
|
149
|
+
'a name is how an app addresses one specific guide, so two guides cannot share one.');
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
namedBy.set(name, at);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
137
155
|
for (const key of bindingKeys(guide)) {
|
|
138
156
|
const previous = claimedBy.get(key);
|
|
139
157
|
if (previous) {
|
|
@@ -149,6 +149,7 @@ function validateOnboardingGuideV2(guide, context) {
|
|
|
149
149
|
validateNetworkScanFlag(guide, warnings);
|
|
150
150
|
validateApplianceBinding(guide, errors, warnings);
|
|
151
151
|
validateUserNotification(guide, warnings);
|
|
152
|
+
validateGuideName(guide, errors, warnings);
|
|
152
153
|
return { ok: errors.length === 0, errors, warnings };
|
|
153
154
|
}
|
|
154
155
|
/**
|
|
@@ -682,6 +683,37 @@ function validateApplianceBinding(guide, errors, warnings) {
|
|
|
682
683
|
'` guide binds an existing appliance.');
|
|
683
684
|
}
|
|
684
685
|
}
|
|
686
|
+
/**
|
|
687
|
+
* Checks {@link EnyoOnboardingV2Guide.name} is usable as a handle.
|
|
688
|
+
*
|
|
689
|
+
* The property is optional — a guide that never needs to be addressed does not
|
|
690
|
+
* need one, and the host falls back to a name derived from the guide's
|
|
691
|
+
* bindings. But a name that is *present* and blank is worse than absent: it
|
|
692
|
+
* looks like a handle at the call site and matches nothing, so
|
|
693
|
+
* `removeOnboardingGuide('')` would silently do nothing. That is an error.
|
|
694
|
+
*
|
|
695
|
+
* Whitespace around a name is trimmed by the host before it is compared, so
|
|
696
|
+
* `" setup "` and `"setup"` are the same handle; a name carrying padding is
|
|
697
|
+
* only warned about, since it works but will not look like what the author
|
|
698
|
+
* typed in a log line.
|
|
699
|
+
*
|
|
700
|
+
* @param guide - The guide being validated.
|
|
701
|
+
* @param errors - Collector for blocking problems.
|
|
702
|
+
* @param warnings - Collector for advisory problems.
|
|
703
|
+
*/
|
|
704
|
+
function validateGuideName(guide, errors, warnings) {
|
|
705
|
+
if (guide.name === undefined)
|
|
706
|
+
return;
|
|
707
|
+
if (typeof guide.name !== 'string' || guide.name.trim() === '') {
|
|
708
|
+
errors.push('`name` is present but blank — a guide either carries a handle an app can address ' +
|
|
709
|
+
'it by or omits the property entirely; an empty name matches nothing.');
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (guide.name !== guide.name.trim()) {
|
|
713
|
+
warnings.push(`\`name\` "${guide.name}" has leading or trailing whitespace, which the host trims — ` +
|
|
714
|
+
'the guide is addressed as `' + guide.name.trim() + '`.');
|
|
715
|
+
}
|
|
716
|
+
}
|
|
685
717
|
/**
|
|
686
718
|
* Checks {@link EnyoOnboardingV2Guide.notifyUser} against the guide's
|
|
687
719
|
* {@link EnyoOnboardingV2Guide.startVariant}.
|
|
@@ -1,6 +1,28 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Which run of a named guide an app means.
|
|
6
|
+
*
|
|
7
|
+
* A guide name identifies the *flow*; this narrows it to one walk of that flow
|
|
8
|
+
* when more than one can be open at a time. A maintenance guide is bound to an
|
|
9
|
+
* appliance and its runs live in a lane of their own — one per appliance — so
|
|
10
|
+
* that is the only distinction the host needs.
|
|
11
|
+
*
|
|
12
|
+
* Omit it entirely for a guide that has one open run, which is every
|
|
13
|
+
* installation variant.
|
|
14
|
+
*/
|
|
15
|
+
export interface EnyoOnboardingV2RunSelector {
|
|
16
|
+
/**
|
|
17
|
+
* The appliance whose run is meant, for a guide bound to one
|
|
18
|
+
* ({@link EnyoOnboardingV2StartVariant.Maintenance}).
|
|
19
|
+
*
|
|
20
|
+
* Omitted on a maintenance guide, the call addresses that guide's single
|
|
21
|
+
* open run when there is exactly one and is rejected when there are
|
|
22
|
+
* several — an ambiguous "complete the run" must not close an arbitrary one.
|
|
23
|
+
*/
|
|
24
|
+
applianceId?: string;
|
|
25
|
+
}
|
|
4
26
|
/**
|
|
5
27
|
* Handler the host calls to collect every onboarding guide (v2) an app ships.
|
|
6
28
|
*
|
|
@@ -196,6 +218,124 @@ export interface EnergyAppOnboardingV2 {
|
|
|
196
218
|
* @returns Promise that resolves once the host has taken the new answer.
|
|
197
219
|
*/
|
|
198
220
|
refreshOnboardingGuides(): Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Retires one guide by name, without the app restating its whole set.
|
|
223
|
+
*
|
|
224
|
+
* The ordinary way to retire a guide is to leave it out of the next
|
|
225
|
+
* {@link EnyoOnboardingV2GuidesHandler} answer, and that remains the right
|
|
226
|
+
* choice when the app's guide set is a static list in its source. This is
|
|
227
|
+
* for the case that answer cannot express: a guide the app decided to
|
|
228
|
+
* withdraw *now* — a vendor account was unlinked, an appliance it was bound
|
|
229
|
+
* to is gone — where waiting for the host's next pull means an installer
|
|
230
|
+
* can still start a flow the app can no longer serve.
|
|
231
|
+
*
|
|
232
|
+
* Names the guide by its {@link EnyoOnboardingV2Guide.name}, so only a
|
|
233
|
+
* guide that carries one can be retired this way. A guide without a name is
|
|
234
|
+
* addressable only as part of the complete answer.
|
|
235
|
+
*
|
|
236
|
+
* **The handler is still the source of truth.** This drops the guide from
|
|
237
|
+
* the host's cache; it does not stop the app from offering it again. If the
|
|
238
|
+
* handler keeps returning the guide, the next pull brings it back — so
|
|
239
|
+
* remove it from what the handler builds as well, or the retirement lasts
|
|
240
|
+
* only until the next sync.
|
|
241
|
+
*
|
|
242
|
+
* Runs already walking the guide are not touched: a guide is retired for the
|
|
243
|
+
* *next* installer, and pulling a flow out from under one already inside it
|
|
244
|
+
* would strand them mid-installation. Use {@link removeOnboardingRun} to end
|
|
245
|
+
* a run.
|
|
246
|
+
*
|
|
247
|
+
* Removing a guide the host does not know — wrong name, already retired — is
|
|
248
|
+
* a no-op rather than an error.
|
|
249
|
+
*
|
|
250
|
+
* This API is available to every app — it is not permission-gated.
|
|
251
|
+
*
|
|
252
|
+
* @param name - The {@link EnyoOnboardingV2Guide.name} of the guide to retire.
|
|
253
|
+
* @returns Promise that resolves once the host has dropped the guide.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* await energyApp.useOnboardingV2().removeOnboardingGuide('vendor-cloud-setup');
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
removeOnboardingGuide(name: string): Promise<void>;
|
|
261
|
+
/**
|
|
262
|
+
* Marks the run walking a named guide as **completed**.
|
|
263
|
+
*
|
|
264
|
+
* For the flow that finished somewhere other than on the installer's
|
|
265
|
+
* screen. A guide normally completes when the walk reaches a success
|
|
266
|
+
* target — the installer taps through to the end. But an app sometimes
|
|
267
|
+
* learns the goal was met by another route: the wallbox connected over OCPP
|
|
268
|
+
* by itself, the vendor cloud confirmed the pairing, the appliance the
|
|
269
|
+
* maintenance run was servicing reported healthy. Leaving that run open
|
|
270
|
+
* shows the customer an installation still in progress that nobody intends
|
|
271
|
+
* to continue.
|
|
272
|
+
*
|
|
273
|
+
* The run is closed as a **success**: it leaves the "open" set, stops being
|
|
274
|
+
* offered for resumption, and counts as a finished installation. Use
|
|
275
|
+
* {@link removeOnboardingRun} for a flow that ended without achieving
|
|
276
|
+
* anything — the two are not interchangeable, and reporting an abandoned
|
|
277
|
+
* setup as completed is how a broken installation looks fine on a dashboard.
|
|
278
|
+
*
|
|
279
|
+
* Completing a run that is already finished — completed or removed — is a
|
|
280
|
+
* no-op: the first outcome stands, and a late confirmation does not reopen
|
|
281
|
+
* or re-close a run. So is naming a guide with no open run at all.
|
|
282
|
+
*
|
|
283
|
+
* Not permission-gated, like the rest of this API: a guide name is resolved
|
|
284
|
+
* within the calling package, so an app can only reach runs of guides it
|
|
285
|
+
* ships itself.
|
|
286
|
+
*
|
|
287
|
+
* @param name - The {@link EnyoOnboardingV2Guide.name} of the guide whose run
|
|
288
|
+
* should be completed.
|
|
289
|
+
* @param selector - Which run, when the guide can have more than one open.
|
|
290
|
+
* @returns Promise that resolves once the host has closed the run.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* // The wallbox connected on its own — the installer never reached the last step.
|
|
295
|
+
* ocpp.onChargePointConnect(async () => {
|
|
296
|
+
* await energyApp.useOnboardingV2().completeOnboardingRun('wallbox-ocpp-setup');
|
|
297
|
+
* });
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
completeOnboardingRun(name: string, selector?: EnyoOnboardingV2RunSelector): Promise<void>;
|
|
301
|
+
/**
|
|
302
|
+
* Ends the run walking a named guide **without** completing it.
|
|
303
|
+
*
|
|
304
|
+
* The counterpart to {@link completeOnboardingRun}, for a flow that is over
|
|
305
|
+
* but achieved nothing: the device it was setting up was removed, the
|
|
306
|
+
* appliance a maintenance run was servicing no longer exists, the app can no
|
|
307
|
+
* longer serve the guide the run is walking. The run leaves the open set as
|
|
308
|
+
* abandoned rather than successful, so it stops being offered for resumption
|
|
309
|
+
* and is not counted as a finished installation.
|
|
310
|
+
*
|
|
311
|
+
* This ends a *run*, not a guide. The guide stays available and a new run
|
|
312
|
+
* can start on it immediately — that is the point when a stuck run is
|
|
313
|
+
* blocking a fresh attempt, since a device can have only one open run per
|
|
314
|
+
* flow. Use {@link removeOnboardingGuide} to withdraw the flow itself.
|
|
315
|
+
*
|
|
316
|
+
* An installer may be looking at the run when it is removed. Do this because
|
|
317
|
+
* the run genuinely cannot continue, not to tidy up: the screen ends the
|
|
318
|
+
* setup under them, and nothing explains why.
|
|
319
|
+
*
|
|
320
|
+
* Removing a run that is already finished, or naming a guide with no open
|
|
321
|
+
* run, is a no-op.
|
|
322
|
+
*
|
|
323
|
+
* Not permission-gated, like the rest of this API: a guide name is resolved
|
|
324
|
+
* within the calling package, so an app can only reach runs of guides it
|
|
325
|
+
* ships itself.
|
|
326
|
+
*
|
|
327
|
+
* @param name - The {@link EnyoOnboardingV2Guide.name} of the guide whose run
|
|
328
|
+
* should be removed.
|
|
329
|
+
* @param selector - Which run, when the guide can have more than one open.
|
|
330
|
+
* @returns Promise that resolves once the host has ended the run.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* // The appliance this maintenance flow services was deleted.
|
|
335
|
+
* await energyApp.useOnboardingV2().removeOnboardingRun('wallbox-service', {applianceId});
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
removeOnboardingRun(name: string, selector?: EnyoOnboardingV2RunSelector): Promise<void>;
|
|
199
339
|
/**
|
|
200
340
|
* Registers the handler the host calls to resolve dynamic block values
|
|
201
341
|
* (`ocpp-url`, `device-ip`) while a step is rendered.
|
|
@@ -851,6 +851,35 @@ export interface EnyoOnboardingV2Step {
|
|
|
851
851
|
* usually bound at registration/publish time and may be omitted here.
|
|
852
852
|
*/
|
|
853
853
|
export interface EnyoOnboardingV2Guide {
|
|
854
|
+
/**
|
|
855
|
+
* Stable handle for this guide — the name the app calls it by when it wants
|
|
856
|
+
* to act on it later.
|
|
857
|
+
*
|
|
858
|
+
* A guide is normally never addressed: guides are pulled, the app's answer
|
|
859
|
+
* is the complete set, and a guide is retired by leaving it out. This exists
|
|
860
|
+
* for the cases where an app has to say something *about one specific
|
|
861
|
+
* guide* instead of restating the whole set — retiring it with
|
|
862
|
+
* {@link EnergyAppOnboardingV2.removeOnboardingGuide}, or completing or
|
|
863
|
+
* removing the run walking it
|
|
864
|
+
* ({@link EnergyAppOnboardingV2.completeOnboardingRun},
|
|
865
|
+
* {@link EnergyAppOnboardingV2.removeOnboardingRun}).
|
|
866
|
+
*
|
|
867
|
+
* **Stable once shipped, like a step's `name`.** A run in progress is
|
|
868
|
+
* anchored to it, so renaming a guide between two package versions strands
|
|
869
|
+
* whatever was paused inside it. Choose a kebab-case slug that describes the
|
|
870
|
+
* flow rather than the hardware it currently applies to
|
|
871
|
+
* (`wallbox-ocpp-setup`, not `acme-ac22`), and keep it when the bindings
|
|
872
|
+
* change.
|
|
873
|
+
*
|
|
874
|
+
* Unique within the app's answer — two guides sharing a name cannot be told
|
|
875
|
+
* apart, and {@link validateOnboardingV2GuidesResult} rejects the answer.
|
|
876
|
+
*
|
|
877
|
+
* Optional. Omit it and the host files the guide under a name derived from
|
|
878
|
+
* what it binds to (vendor, models, start variant, and the appliance on a
|
|
879
|
+
* maintenance guide) exactly as before — that keeps working, it just cannot
|
|
880
|
+
* be named by an app that never chose a name.
|
|
881
|
+
*/
|
|
882
|
+
name?: string;
|
|
854
883
|
/** Translated display title (de/en). */
|
|
855
884
|
title: EnyoOnboardingTranslatedContent[];
|
|
856
885
|
/** Which start situation this guide covers. */
|
package/dist/cjs/version.cjs
CHANGED
package/dist/cjs/version.d.cts
CHANGED
|
@@ -41,14 +41,18 @@ const ANY_MODEL = '*';
|
|
|
41
41
|
/**
|
|
42
42
|
* A short human-readable label for a guide, for use in messages.
|
|
43
43
|
*
|
|
44
|
-
* Prefers
|
|
45
|
-
*
|
|
44
|
+
* Prefers {@link EnyoOnboardingV2Guide.name} — it is the handle the author will
|
|
45
|
+
* search for — then the first translated title, then the start variant, so a
|
|
46
|
+
* guide with neither a name nor a title is still identifiable.
|
|
46
47
|
*
|
|
47
48
|
* @param guide - The guide to label.
|
|
48
49
|
* @param index - Its position in the answer's `guides` array.
|
|
49
50
|
* @returns A message prefix such as ``guides[2] ("Wallbox über OCPP")``.
|
|
50
51
|
*/
|
|
51
52
|
function guideLabel(guide, index) {
|
|
53
|
+
const name = guide.name?.trim();
|
|
54
|
+
if (name)
|
|
55
|
+
return `guides[${index}] ("${name}")`;
|
|
52
56
|
const title = guide.title?.[0]?.value;
|
|
53
57
|
return `guides[${index}] (${title ? `"${title}"` : (guide.startVariant ?? '?')})`;
|
|
54
58
|
}
|
|
@@ -116,6 +120,9 @@ export function validateOnboardingV2GuidesResult(result, context) {
|
|
|
116
120
|
}
|
|
117
121
|
// Which guide(s) claimed each binding, so a collision can name both sides.
|
|
118
122
|
const claimedBy = new Map();
|
|
123
|
+
// Same, for the explicit `name` handles — a duplicate makes an app unable to
|
|
124
|
+
// say which of the two it means.
|
|
125
|
+
const namedBy = new Map();
|
|
119
126
|
for (const [i, guide] of result.guides.entries()) {
|
|
120
127
|
const at = guideLabel(guide, i);
|
|
121
128
|
const guideResult = validateOnboardingGuideV2(guide, context);
|
|
@@ -128,6 +135,17 @@ export function validateOnboardingV2GuidesResult(result, context) {
|
|
|
128
135
|
else if (!guide.modelIds?.length) {
|
|
129
136
|
warnings.push(`${at}: no modelIds — this guide applies to every model of "${guide.vendorId}".`);
|
|
130
137
|
}
|
|
138
|
+
const name = guide.name?.trim();
|
|
139
|
+
if (name) {
|
|
140
|
+
const previouslyNamed = namedBy.get(name);
|
|
141
|
+
if (previouslyNamed) {
|
|
142
|
+
errors.push(`${at}: name "${name}" is already used by ${previouslyNamed} — ` +
|
|
143
|
+
'a name is how an app addresses one specific guide, so two guides cannot share one.');
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
namedBy.set(name, at);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
131
149
|
for (const key of bindingKeys(guide)) {
|
|
132
150
|
const previous = claimedBy.get(key);
|
|
133
151
|
if (previous) {
|
|
@@ -143,6 +143,7 @@ export function validateOnboardingGuideV2(guide, context) {
|
|
|
143
143
|
validateNetworkScanFlag(guide, warnings);
|
|
144
144
|
validateApplianceBinding(guide, errors, warnings);
|
|
145
145
|
validateUserNotification(guide, warnings);
|
|
146
|
+
validateGuideName(guide, errors, warnings);
|
|
146
147
|
return { ok: errors.length === 0, errors, warnings };
|
|
147
148
|
}
|
|
148
149
|
/**
|
|
@@ -676,6 +677,37 @@ function validateApplianceBinding(guide, errors, warnings) {
|
|
|
676
677
|
'` guide binds an existing appliance.');
|
|
677
678
|
}
|
|
678
679
|
}
|
|
680
|
+
/**
|
|
681
|
+
* Checks {@link EnyoOnboardingV2Guide.name} is usable as a handle.
|
|
682
|
+
*
|
|
683
|
+
* The property is optional — a guide that never needs to be addressed does not
|
|
684
|
+
* need one, and the host falls back to a name derived from the guide's
|
|
685
|
+
* bindings. But a name that is *present* and blank is worse than absent: it
|
|
686
|
+
* looks like a handle at the call site and matches nothing, so
|
|
687
|
+
* `removeOnboardingGuide('')` would silently do nothing. That is an error.
|
|
688
|
+
*
|
|
689
|
+
* Whitespace around a name is trimmed by the host before it is compared, so
|
|
690
|
+
* `" setup "` and `"setup"` are the same handle; a name carrying padding is
|
|
691
|
+
* only warned about, since it works but will not look like what the author
|
|
692
|
+
* typed in a log line.
|
|
693
|
+
*
|
|
694
|
+
* @param guide - The guide being validated.
|
|
695
|
+
* @param errors - Collector for blocking problems.
|
|
696
|
+
* @param warnings - Collector for advisory problems.
|
|
697
|
+
*/
|
|
698
|
+
function validateGuideName(guide, errors, warnings) {
|
|
699
|
+
if (guide.name === undefined)
|
|
700
|
+
return;
|
|
701
|
+
if (typeof guide.name !== 'string' || guide.name.trim() === '') {
|
|
702
|
+
errors.push('`name` is present but blank — a guide either carries a handle an app can address ' +
|
|
703
|
+
'it by or omits the property entirely; an empty name matches nothing.');
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (guide.name !== guide.name.trim()) {
|
|
707
|
+
warnings.push(`\`name\` "${guide.name}" has leading or trailing whitespace, which the host trims — ` +
|
|
708
|
+
'the guide is addressed as `' + guide.name.trim() + '`.');
|
|
709
|
+
}
|
|
710
|
+
}
|
|
679
711
|
/**
|
|
680
712
|
* Checks {@link EnyoOnboardingV2Guide.notifyUser} against the guide's
|
|
681
713
|
* {@link EnyoOnboardingV2Guide.startVariant}.
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import type { EnyoOnboardingV2GuidesRequest, EnyoOnboardingV2GuidesResult } from '../types/enyo-onboarding-v2-provider.js';
|
|
2
2
|
import type { EnyoOnboardingV2DynamicRequest, EnyoOnboardingV2DynamicResult } from '../types/enyo-onboarding-v2-dynamic.js';
|
|
3
3
|
import type { EnyoOnboardingV2AdditionalSetupRequest, EnyoOnboardingV2AdditionalSetupResult } from '../types/enyo-onboarding-v2-additional-setup.js';
|
|
4
|
+
/**
|
|
5
|
+
* Which run of a named guide an app means.
|
|
6
|
+
*
|
|
7
|
+
* A guide name identifies the *flow*; this narrows it to one walk of that flow
|
|
8
|
+
* when more than one can be open at a time. A maintenance guide is bound to an
|
|
9
|
+
* appliance and its runs live in a lane of their own — one per appliance — so
|
|
10
|
+
* that is the only distinction the host needs.
|
|
11
|
+
*
|
|
12
|
+
* Omit it entirely for a guide that has one open run, which is every
|
|
13
|
+
* installation variant.
|
|
14
|
+
*/
|
|
15
|
+
export interface EnyoOnboardingV2RunSelector {
|
|
16
|
+
/**
|
|
17
|
+
* The appliance whose run is meant, for a guide bound to one
|
|
18
|
+
* ({@link EnyoOnboardingV2StartVariant.Maintenance}).
|
|
19
|
+
*
|
|
20
|
+
* Omitted on a maintenance guide, the call addresses that guide's single
|
|
21
|
+
* open run when there is exactly one and is rejected when there are
|
|
22
|
+
* several — an ambiguous "complete the run" must not close an arbitrary one.
|
|
23
|
+
*/
|
|
24
|
+
applianceId?: string;
|
|
25
|
+
}
|
|
4
26
|
/**
|
|
5
27
|
* Handler the host calls to collect every onboarding guide (v2) an app ships.
|
|
6
28
|
*
|
|
@@ -196,6 +218,124 @@ export interface EnergyAppOnboardingV2 {
|
|
|
196
218
|
* @returns Promise that resolves once the host has taken the new answer.
|
|
197
219
|
*/
|
|
198
220
|
refreshOnboardingGuides(): Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Retires one guide by name, without the app restating its whole set.
|
|
223
|
+
*
|
|
224
|
+
* The ordinary way to retire a guide is to leave it out of the next
|
|
225
|
+
* {@link EnyoOnboardingV2GuidesHandler} answer, and that remains the right
|
|
226
|
+
* choice when the app's guide set is a static list in its source. This is
|
|
227
|
+
* for the case that answer cannot express: a guide the app decided to
|
|
228
|
+
* withdraw *now* — a vendor account was unlinked, an appliance it was bound
|
|
229
|
+
* to is gone — where waiting for the host's next pull means an installer
|
|
230
|
+
* can still start a flow the app can no longer serve.
|
|
231
|
+
*
|
|
232
|
+
* Names the guide by its {@link EnyoOnboardingV2Guide.name}, so only a
|
|
233
|
+
* guide that carries one can be retired this way. A guide without a name is
|
|
234
|
+
* addressable only as part of the complete answer.
|
|
235
|
+
*
|
|
236
|
+
* **The handler is still the source of truth.** This drops the guide from
|
|
237
|
+
* the host's cache; it does not stop the app from offering it again. If the
|
|
238
|
+
* handler keeps returning the guide, the next pull brings it back — so
|
|
239
|
+
* remove it from what the handler builds as well, or the retirement lasts
|
|
240
|
+
* only until the next sync.
|
|
241
|
+
*
|
|
242
|
+
* Runs already walking the guide are not touched: a guide is retired for the
|
|
243
|
+
* *next* installer, and pulling a flow out from under one already inside it
|
|
244
|
+
* would strand them mid-installation. Use {@link removeOnboardingRun} to end
|
|
245
|
+
* a run.
|
|
246
|
+
*
|
|
247
|
+
* Removing a guide the host does not know — wrong name, already retired — is
|
|
248
|
+
* a no-op rather than an error.
|
|
249
|
+
*
|
|
250
|
+
* This API is available to every app — it is not permission-gated.
|
|
251
|
+
*
|
|
252
|
+
* @param name - The {@link EnyoOnboardingV2Guide.name} of the guide to retire.
|
|
253
|
+
* @returns Promise that resolves once the host has dropped the guide.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* await energyApp.useOnboardingV2().removeOnboardingGuide('vendor-cloud-setup');
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
removeOnboardingGuide(name: string): Promise<void>;
|
|
261
|
+
/**
|
|
262
|
+
* Marks the run walking a named guide as **completed**.
|
|
263
|
+
*
|
|
264
|
+
* For the flow that finished somewhere other than on the installer's
|
|
265
|
+
* screen. A guide normally completes when the walk reaches a success
|
|
266
|
+
* target — the installer taps through to the end. But an app sometimes
|
|
267
|
+
* learns the goal was met by another route: the wallbox connected over OCPP
|
|
268
|
+
* by itself, the vendor cloud confirmed the pairing, the appliance the
|
|
269
|
+
* maintenance run was servicing reported healthy. Leaving that run open
|
|
270
|
+
* shows the customer an installation still in progress that nobody intends
|
|
271
|
+
* to continue.
|
|
272
|
+
*
|
|
273
|
+
* The run is closed as a **success**: it leaves the "open" set, stops being
|
|
274
|
+
* offered for resumption, and counts as a finished installation. Use
|
|
275
|
+
* {@link removeOnboardingRun} for a flow that ended without achieving
|
|
276
|
+
* anything — the two are not interchangeable, and reporting an abandoned
|
|
277
|
+
* setup as completed is how a broken installation looks fine on a dashboard.
|
|
278
|
+
*
|
|
279
|
+
* Completing a run that is already finished — completed or removed — is a
|
|
280
|
+
* no-op: the first outcome stands, and a late confirmation does not reopen
|
|
281
|
+
* or re-close a run. So is naming a guide with no open run at all.
|
|
282
|
+
*
|
|
283
|
+
* Not permission-gated, like the rest of this API: a guide name is resolved
|
|
284
|
+
* within the calling package, so an app can only reach runs of guides it
|
|
285
|
+
* ships itself.
|
|
286
|
+
*
|
|
287
|
+
* @param name - The {@link EnyoOnboardingV2Guide.name} of the guide whose run
|
|
288
|
+
* should be completed.
|
|
289
|
+
* @param selector - Which run, when the guide can have more than one open.
|
|
290
|
+
* @returns Promise that resolves once the host has closed the run.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* // The wallbox connected on its own — the installer never reached the last step.
|
|
295
|
+
* ocpp.onChargePointConnect(async () => {
|
|
296
|
+
* await energyApp.useOnboardingV2().completeOnboardingRun('wallbox-ocpp-setup');
|
|
297
|
+
* });
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
completeOnboardingRun(name: string, selector?: EnyoOnboardingV2RunSelector): Promise<void>;
|
|
301
|
+
/**
|
|
302
|
+
* Ends the run walking a named guide **without** completing it.
|
|
303
|
+
*
|
|
304
|
+
* The counterpart to {@link completeOnboardingRun}, for a flow that is over
|
|
305
|
+
* but achieved nothing: the device it was setting up was removed, the
|
|
306
|
+
* appliance a maintenance run was servicing no longer exists, the app can no
|
|
307
|
+
* longer serve the guide the run is walking. The run leaves the open set as
|
|
308
|
+
* abandoned rather than successful, so it stops being offered for resumption
|
|
309
|
+
* and is not counted as a finished installation.
|
|
310
|
+
*
|
|
311
|
+
* This ends a *run*, not a guide. The guide stays available and a new run
|
|
312
|
+
* can start on it immediately — that is the point when a stuck run is
|
|
313
|
+
* blocking a fresh attempt, since a device can have only one open run per
|
|
314
|
+
* flow. Use {@link removeOnboardingGuide} to withdraw the flow itself.
|
|
315
|
+
*
|
|
316
|
+
* An installer may be looking at the run when it is removed. Do this because
|
|
317
|
+
* the run genuinely cannot continue, not to tidy up: the screen ends the
|
|
318
|
+
* setup under them, and nothing explains why.
|
|
319
|
+
*
|
|
320
|
+
* Removing a run that is already finished, or naming a guide with no open
|
|
321
|
+
* run, is a no-op.
|
|
322
|
+
*
|
|
323
|
+
* Not permission-gated, like the rest of this API: a guide name is resolved
|
|
324
|
+
* within the calling package, so an app can only reach runs of guides it
|
|
325
|
+
* ships itself.
|
|
326
|
+
*
|
|
327
|
+
* @param name - The {@link EnyoOnboardingV2Guide.name} of the guide whose run
|
|
328
|
+
* should be removed.
|
|
329
|
+
* @param selector - Which run, when the guide can have more than one open.
|
|
330
|
+
* @returns Promise that resolves once the host has ended the run.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* // The appliance this maintenance flow services was deleted.
|
|
335
|
+
* await energyApp.useOnboardingV2().removeOnboardingRun('wallbox-service', {applianceId});
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
removeOnboardingRun(name: string, selector?: EnyoOnboardingV2RunSelector): Promise<void>;
|
|
199
339
|
/**
|
|
200
340
|
* Registers the handler the host calls to resolve dynamic block values
|
|
201
341
|
* (`ocpp-url`, `device-ip`) while a step is rendered.
|
|
@@ -851,6 +851,35 @@ export interface EnyoOnboardingV2Step {
|
|
|
851
851
|
* usually bound at registration/publish time and may be omitted here.
|
|
852
852
|
*/
|
|
853
853
|
export interface EnyoOnboardingV2Guide {
|
|
854
|
+
/**
|
|
855
|
+
* Stable handle for this guide — the name the app calls it by when it wants
|
|
856
|
+
* to act on it later.
|
|
857
|
+
*
|
|
858
|
+
* A guide is normally never addressed: guides are pulled, the app's answer
|
|
859
|
+
* is the complete set, and a guide is retired by leaving it out. This exists
|
|
860
|
+
* for the cases where an app has to say something *about one specific
|
|
861
|
+
* guide* instead of restating the whole set — retiring it with
|
|
862
|
+
* {@link EnergyAppOnboardingV2.removeOnboardingGuide}, or completing or
|
|
863
|
+
* removing the run walking it
|
|
864
|
+
* ({@link EnergyAppOnboardingV2.completeOnboardingRun},
|
|
865
|
+
* {@link EnergyAppOnboardingV2.removeOnboardingRun}).
|
|
866
|
+
*
|
|
867
|
+
* **Stable once shipped, like a step's `name`.** A run in progress is
|
|
868
|
+
* anchored to it, so renaming a guide between two package versions strands
|
|
869
|
+
* whatever was paused inside it. Choose a kebab-case slug that describes the
|
|
870
|
+
* flow rather than the hardware it currently applies to
|
|
871
|
+
* (`wallbox-ocpp-setup`, not `acme-ac22`), and keep it when the bindings
|
|
872
|
+
* change.
|
|
873
|
+
*
|
|
874
|
+
* Unique within the app's answer — two guides sharing a name cannot be told
|
|
875
|
+
* apart, and {@link validateOnboardingV2GuidesResult} rejects the answer.
|
|
876
|
+
*
|
|
877
|
+
* Optional. Omit it and the host files the guide under a name derived from
|
|
878
|
+
* what it binds to (vendor, models, start variant, and the appliance on a
|
|
879
|
+
* maintenance guide) exactly as before — that keeps working, it just cannot
|
|
880
|
+
* be named by an app that never chose a name.
|
|
881
|
+
*/
|
|
882
|
+
name?: string;
|
|
854
883
|
/** Translated display title (de/en). */
|
|
855
884
|
title: EnyoOnboardingTranslatedContent[];
|
|
856
885
|
/** Which start situation this guide covers. */
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED