@enyo-energy/energy-app-sdk 1.5.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/README.md +45 -0
- package/dist/cjs/energy-app-permission.type.cjs +1 -0
- package/dist/cjs/energy-app-permission.type.d.cts +2 -1
- package/dist/cjs/energy-app.cjs +16 -0
- package/dist/cjs/energy-app.d.cts +15 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
- 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 +4 -0
- package/dist/cjs/index.d.cts +4 -0
- package/dist/cjs/packages/energy-app-command-log.cjs +2 -0
- package/dist/cjs/packages/energy-app-command-log.d.cts +108 -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-battery-appliance.d.cts +23 -0
- package/dist/cjs/types/enyo-command-log.cjs +50 -0
- package/dist/cjs/types/enyo-command-log.d.cts +151 -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/energy-app-permission.type.d.ts +2 -1
- package/dist/energy-app-permission.type.js +1 -0
- package/dist/energy-app.d.ts +15 -0
- package/dist/energy-app.js +16 -0
- package/dist/enyo-energy-app-sdk.d.ts +3 -0
- 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 +4 -0
- package/dist/index.js +4 -0
- package/dist/packages/energy-app-command-log.d.ts +108 -0
- package/dist/packages/energy-app-command-log.js +1 -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-battery-appliance.d.ts +23 -0
- package/dist/types/enyo-command-log.d.ts +151 -0
- package/dist/types/enyo-command-log.js +47 -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
package/README.md
CHANGED
|
@@ -367,6 +367,7 @@ Energy Apps use a granular permissions system to control access to system resour
|
|
|
367
367
|
- **`Savings`**: Publish and read back day-scoped savings reports
|
|
368
368
|
- **`EpexSpotPrices`**: Read EPEX SPOT day-ahead wholesale market prices
|
|
369
369
|
- **`GridFeeRegister`** / **`GridFeeUse`**: Publish / resolve dynamic grid fees (time-variable network charges)
|
|
370
|
+
- **`CommandLog`**: Record and read back the commands apps issued to appliances
|
|
370
371
|
|
|
371
372
|
#### Site & Identity Permissions
|
|
372
373
|
|
|
@@ -1500,6 +1501,50 @@ Notes worth respecting when producing a report:
|
|
|
1500
1501
|
|
|
1501
1502
|
Requires the `Savings` permission.
|
|
1502
1503
|
|
|
1504
|
+
#### `useCommandLog(): EnergyAppCommandLog`
|
|
1505
|
+
|
|
1506
|
+
Record which register, configuration key or message your app wrote to an appliance — and read those
|
|
1507
|
+
writes back. This is the answer to *"why did the device do that?"*: an appliance's state shows what it
|
|
1508
|
+
is doing now, not that you wrote holding register 40023 four seconds ago because the energy manager cut
|
|
1509
|
+
available power, nor that the write was rejected.
|
|
1510
|
+
|
|
1511
|
+
```typescript
|
|
1512
|
+
const commandLog = energyApp.useCommandLog();
|
|
1513
|
+
|
|
1514
|
+
await commandLog.logCommand({
|
|
1515
|
+
applianceId,
|
|
1516
|
+
protocol: EnyoCommandProtocolEnum.Modbus,
|
|
1517
|
+
operation: 'write-single-register',
|
|
1518
|
+
target: '40023',
|
|
1519
|
+
previousValue: String(before),
|
|
1520
|
+
newValue: String(limitW),
|
|
1521
|
+
outcome: EnyoCommandOutcomeEnum.Success,
|
|
1522
|
+
reason: 'energy manager reduced available power',
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
// Everything we failed to write to this appliance today.
|
|
1526
|
+
const failures = await commandLog.getCommands({
|
|
1527
|
+
applianceId,
|
|
1528
|
+
outcome: EnyoCommandOutcomeEnum.Failed,
|
|
1529
|
+
fromIso: startOfDay,
|
|
1530
|
+
});
|
|
1531
|
+
```
|
|
1532
|
+
|
|
1533
|
+
- **Writes, not reads.** Polling a register every two seconds does not belong here; recording reads
|
|
1534
|
+
would bury the writes the log exists for.
|
|
1535
|
+
- **Log the attempt, not only the success.** `Failed`, `TimedOut` and `Rejected` entries are the ones
|
|
1536
|
+
someone goes looking for. `Rejected` is separate from `Failed` on purpose — a device that answered and
|
|
1537
|
+
refused is a different diagnosis from one that could not be reached.
|
|
1538
|
+
- **Fill `reason`.** *What* was written can be reconstructed from the device; *why* cannot.
|
|
1539
|
+
- **Never gate the command on the log.** Log after the write is attempted; a failure to log must not
|
|
1540
|
+
surface as a failure to control the appliance.
|
|
1541
|
+
- **Append-only.** No update, no delete — a log the app can rewrite cannot explain an incident.
|
|
1542
|
+
Retention is the host's.
|
|
1543
|
+
- **Reads span every app** unless you filter by `packageName`: two packages fighting over the same
|
|
1544
|
+
register are invisible from one app's own entries.
|
|
1545
|
+
|
|
1546
|
+
Requires the `CommandLog` permission.
|
|
1547
|
+
|
|
1503
1548
|
### Operational Utilities
|
|
1504
1549
|
|
|
1505
1550
|
#### `useOnboardingV2(): EnergyAppOnboardingV2`
|
|
@@ -62,4 +62,5 @@ var EnergyAppPermissionTypeEnum;
|
|
|
62
62
|
EnergyAppPermissionTypeEnum["FirmwareRegistry"] = "FirmwareRegistry";
|
|
63
63
|
EnergyAppPermissionTypeEnum["GridFeeRegister"] = "GridFeeRegister";
|
|
64
64
|
EnergyAppPermissionTypeEnum["GridFeeUse"] = "GridFeeUse";
|
|
65
|
+
EnergyAppPermissionTypeEnum["CommandLog"] = "CommandLog";
|
|
65
66
|
})(EnergyAppPermissionTypeEnum || (exports.EnergyAppPermissionTypeEnum = EnergyAppPermissionTypeEnum = {}));
|
|
@@ -57,7 +57,8 @@ export declare enum EnergyAppPermissionTypeEnum {
|
|
|
57
57
|
EpexSpotPrices = "EpexSpotPrices",
|
|
58
58
|
FirmwareRegistry = "FirmwareRegistry",
|
|
59
59
|
GridFeeRegister = "GridFeeRegister",
|
|
60
|
-
GridFeeUse = "GridFeeUse"
|
|
60
|
+
GridFeeUse = "GridFeeUse",
|
|
61
|
+
CommandLog = "CommandLog"
|
|
61
62
|
}
|
|
62
63
|
/**
|
|
63
64
|
* String union of every permission name, derived from
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -465,6 +465,22 @@ class EnergyApp {
|
|
|
465
465
|
useGridFee() {
|
|
466
466
|
return this.energyAppSdk.useGridFee();
|
|
467
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* Gets the Command Log API for recording the writes this app issued to an
|
|
470
|
+
* appliance — which Modbus register, which OCPP configuration key or
|
|
471
|
+
* message, with what it was before and whether the device took it.
|
|
472
|
+
*
|
|
473
|
+
* An appliance's state shows what it is doing; the command log shows what
|
|
474
|
+
* was done to it and why, which is what an incident investigation actually
|
|
475
|
+
* needs. Entries are append-only, and reads span every app so that two
|
|
476
|
+
* packages fighting over the same register are visible.
|
|
477
|
+
* @returns The Command Log API instance
|
|
478
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `CommandLog`
|
|
479
|
+
* permission is not granted.
|
|
480
|
+
*/
|
|
481
|
+
useCommandLog() {
|
|
482
|
+
return this.energyAppSdk.useCommandLog();
|
|
483
|
+
}
|
|
468
484
|
/**
|
|
469
485
|
* Gets the current SDK version.
|
|
470
486
|
* @returns The semantic version string of the SDK
|
|
@@ -46,6 +46,7 @@ import { EnergyAppSavings } from "./packages/energy-app-savings.cjs";
|
|
|
46
46
|
import { EnergyAppDeviceTest } from "./packages/energy-app-device-test.cjs";
|
|
47
47
|
import { EnergyAppEpexSpotPrice } from "./packages/energy-app-epex-spot-price.cjs";
|
|
48
48
|
import { EnergyAppGridFee } from "./packages/energy-app-grid-fee.cjs";
|
|
49
|
+
import { EnergyAppCommandLog } from "./packages/energy-app-command-log.cjs";
|
|
49
50
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
50
51
|
/**
|
|
51
52
|
* Concrete implementation of {@link EnyoEnergyAppSdk} that delegates every call
|
|
@@ -387,6 +388,20 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
387
388
|
* `GridFeeUse` permission required by the called method is not granted.
|
|
388
389
|
*/
|
|
389
390
|
useGridFee(): EnergyAppGridFee;
|
|
391
|
+
/**
|
|
392
|
+
* Gets the Command Log API for recording the writes this app issued to an
|
|
393
|
+
* appliance — which Modbus register, which OCPP configuration key or
|
|
394
|
+
* message, with what it was before and whether the device took it.
|
|
395
|
+
*
|
|
396
|
+
* An appliance's state shows what it is doing; the command log shows what
|
|
397
|
+
* was done to it and why, which is what an incident investigation actually
|
|
398
|
+
* needs. Entries are append-only, and reads span every app so that two
|
|
399
|
+
* packages fighting over the same register are visible.
|
|
400
|
+
* @returns The Command Log API instance
|
|
401
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `CommandLog`
|
|
402
|
+
* permission is not granted.
|
|
403
|
+
*/
|
|
404
|
+
useCommandLog(): EnergyAppCommandLog;
|
|
390
405
|
/**
|
|
391
406
|
* Gets the current SDK version.
|
|
392
407
|
* @returns The semantic version string of the SDK
|
|
@@ -45,6 +45,7 @@ import { EnergyAppSavings } from "./packages/energy-app-savings.cjs";
|
|
|
45
45
|
import { EnergyAppDeviceTest } from "./packages/energy-app-device-test.cjs";
|
|
46
46
|
import { EnergyAppEpexSpotPrice } from "./packages/energy-app-epex-spot-price.cjs";
|
|
47
47
|
import { EnergyAppGridFee } from "./packages/energy-app-grid-fee.cjs";
|
|
48
|
+
import { EnergyAppCommandLog } from "./packages/energy-app-command-log.cjs";
|
|
48
49
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
49
50
|
export declare enum EnergyAppStateEnum {
|
|
50
51
|
Launching = "launching",
|
|
@@ -166,4 +167,6 @@ export interface EnyoEnergyAppSdk {
|
|
|
166
167
|
useEpexSpotPrices: () => EnergyAppEpexSpotPrice;
|
|
167
168
|
/** Get the Grid Fee API for publishing time-dependent network charges and resolving them to a 15-minute series */
|
|
168
169
|
useGridFee: () => EnergyAppGridFee;
|
|
170
|
+
/** Get the Command Log API for recording which register, configuration key or message this app wrote to an appliance, and reading those commands back */
|
|
171
|
+
useCommandLog: () => EnergyAppCommandLog;
|
|
169
172
|
}
|
|
@@ -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);
|
|
@@ -147,6 +149,8 @@ __exportStar(require("./packages/energy-app-device-test.cjs"), exports);
|
|
|
147
149
|
__exportStar(require("./implementations/device-test/device-test-validators.cjs"), exports);
|
|
148
150
|
__exportStar(require("./types/enyo-savings.cjs"), exports);
|
|
149
151
|
__exportStar(require("./packages/energy-app-savings.cjs"), exports);
|
|
152
|
+
__exportStar(require("./types/enyo-command-log.cjs"), exports);
|
|
153
|
+
__exportStar(require("./packages/energy-app-command-log.cjs"), exports);
|
|
150
154
|
__exportStar(require("./types/enyo-epex-spot-price.cjs"), exports);
|
|
151
155
|
__exportStar(require("./packages/energy-app-epex-spot-price.cjs"), exports);
|
|
152
156
|
__exportStar(require("./types/enyo-automation.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';
|
|
@@ -131,6 +133,8 @@ export * from './packages/energy-app-device-test.cjs';
|
|
|
131
133
|
export * from './implementations/device-test/device-test-validators.cjs';
|
|
132
134
|
export * from './types/enyo-savings.cjs';
|
|
133
135
|
export * from './packages/energy-app-savings.cjs';
|
|
136
|
+
export * from './types/enyo-command-log.cjs';
|
|
137
|
+
export * from './packages/energy-app-command-log.cjs';
|
|
134
138
|
export * from './types/enyo-epex-spot-price.cjs';
|
|
135
139
|
export * from './packages/energy-app-epex-spot-price.cjs';
|
|
136
140
|
export * from './types/enyo-automation.cjs';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { EnyoCommandLogEntry, EnyoCommandLogEntryInput, EnyoCommandLogFilter } from "../types/enyo-command-log.cjs";
|
|
2
|
+
/**
|
|
3
|
+
* Interface for recording and reading back the commands an energy app issued to
|
|
4
|
+
* an appliance.
|
|
5
|
+
*
|
|
6
|
+
* This is the answer to "why did the device do that?". An appliance's own state
|
|
7
|
+
* shows what it is doing now; it does not show that this app wrote holding
|
|
8
|
+
* register 40023 four seconds ago because the energy manager cut the available
|
|
9
|
+
* power, nor that the write was rejected. Without a record of the writes, that
|
|
10
|
+
* has to be reconstructed from log files that may already have rotated away.
|
|
11
|
+
*
|
|
12
|
+
* The unit is a **write, not a read**. Polling a register every two seconds does
|
|
13
|
+
* not belong here — an entry is a command that changed, or tried to change,
|
|
14
|
+
* something on the device. Recording reads would bury the writes.
|
|
15
|
+
*
|
|
16
|
+
* Entries are append-only: there is no update and no delete. A log an app can
|
|
17
|
+
* rewrite cannot be trusted to explain an incident, and retention is the host's
|
|
18
|
+
* to enforce.
|
|
19
|
+
*
|
|
20
|
+
* Access to this API requires the `CommandLog` permission
|
|
21
|
+
* ({@link EnergyAppPermissionType}); {@link EnergyApp.useCommandLog} throws when
|
|
22
|
+
* it has not been granted.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const commandLog = energyApp.useCommandLog();
|
|
27
|
+
*
|
|
28
|
+
* const before = await modbus.readHoldingRegisters(40023, 1);
|
|
29
|
+
* try {
|
|
30
|
+
* await modbus.writeSingleRegister(40023, limitW);
|
|
31
|
+
* await commandLog.logCommand({
|
|
32
|
+
* applianceId,
|
|
33
|
+
* protocol: EnyoCommandProtocolEnum.Modbus,
|
|
34
|
+
* operation: 'write-single-register',
|
|
35
|
+
* target: '40023',
|
|
36
|
+
* previousValue: String(before.readUInt16BE(0)),
|
|
37
|
+
* newValue: String(limitW),
|
|
38
|
+
* outcome: EnyoCommandOutcomeEnum.Success,
|
|
39
|
+
* reason: 'energy manager reduced available power',
|
|
40
|
+
* });
|
|
41
|
+
* } catch (error) {
|
|
42
|
+
* await commandLog.logCommand({
|
|
43
|
+
* applianceId,
|
|
44
|
+
* protocol: EnyoCommandProtocolEnum.Modbus,
|
|
45
|
+
* operation: 'write-single-register',
|
|
46
|
+
* target: '40023',
|
|
47
|
+
* newValue: String(limitW),
|
|
48
|
+
* outcome: EnyoCommandOutcomeEnum.Failed,
|
|
49
|
+
* errorMessage: String(error),
|
|
50
|
+
* reason: 'energy manager reduced available power',
|
|
51
|
+
* });
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export interface EnergyAppCommandLog {
|
|
56
|
+
/**
|
|
57
|
+
* Appends one command to the log.
|
|
58
|
+
*
|
|
59
|
+
* Log the attempt, not only the success: a command that timed out or was
|
|
60
|
+
* rejected is the entry someone will actually go looking for, and an app
|
|
61
|
+
* that only records its successes produces a log where nothing ever went
|
|
62
|
+
* wrong. Set {@link EnyoCommandLogEntry.outcome} accordingly and put the
|
|
63
|
+
* detail in `errorMessage`.
|
|
64
|
+
*
|
|
65
|
+
* Fill {@link EnyoCommandLogEntry.reason} whenever the app knows it. What was
|
|
66
|
+
* written can be reconstructed from the device; why it was written cannot.
|
|
67
|
+
*
|
|
68
|
+
* This call is for auditing and must never gate the command itself — log
|
|
69
|
+
* after the write has been attempted, and do not let a failure to log
|
|
70
|
+
* surface as a failure to control the appliance.
|
|
71
|
+
*
|
|
72
|
+
* Requires the `CommandLog` permission.
|
|
73
|
+
*
|
|
74
|
+
* @param entry - The command to record. `id`, `packageName` and (unless
|
|
75
|
+
* supplied) `timestampIso` are assigned by the host.
|
|
76
|
+
* @returns Promise resolving to the stored entry, including its assigned id
|
|
77
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `CommandLog` permission
|
|
78
|
+
* is not granted.
|
|
79
|
+
*/
|
|
80
|
+
logCommand(entry: EnyoCommandLogEntryInput): Promise<EnyoCommandLogEntry>;
|
|
81
|
+
/**
|
|
82
|
+
* Reads recorded commands back, newest first.
|
|
83
|
+
*
|
|
84
|
+
* Returns entries from **every** app, not just this one, unless
|
|
85
|
+
* {@link EnyoCommandLogFilter.packageName} narrows it. That is deliberate:
|
|
86
|
+
* the failure this log exists to explain is often two apps writing the same
|
|
87
|
+
* register, which is invisible from either app's own entries.
|
|
88
|
+
*
|
|
89
|
+
* Requires the `CommandLog` permission.
|
|
90
|
+
*
|
|
91
|
+
* @param filter - Optional narrowing by appliance, protocol, outcome, time
|
|
92
|
+
* range or package. Fields combine with AND.
|
|
93
|
+
* @returns Promise resolving to the matching entries, newest first
|
|
94
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `CommandLog` permission
|
|
95
|
+
* is not granted.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* // Everything we failed to write to this appliance today.
|
|
100
|
+
* const failures = await commandLog.getCommands({
|
|
101
|
+
* applianceId,
|
|
102
|
+
* outcome: EnyoCommandOutcomeEnum.Failed,
|
|
103
|
+
* fromIso: startOfDay,
|
|
104
|
+
* });
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
getCommands(filter?: EnyoCommandLogFilter): Promise<EnyoCommandLogEntry[]>;
|
|
108
|
+
}
|
|
@@ -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.
|