@enyo-energy/energy-app-sdk 1.44.0 → 1.45.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 +141 -1
- package/dist/cjs/energy-app-package-definition.d.cts +46 -1
- package/dist/cjs/implementations/heatpump/heatpump-metadata-validators.cjs +79 -1
- package/dist/cjs/implementations/heatpump/heatpump-metadata-validators.d.cts +4 -2
- package/dist/cjs/implementations/subscriptions/define-subscription-plan.cjs +48 -0
- package/dist/cjs/implementations/subscriptions/define-subscription-plan.d.cts +40 -0
- package/dist/cjs/implementations/subscriptions/subscription-offer-validators.cjs +301 -0
- package/dist/cjs/implementations/subscriptions/subscription-offer-validators.d.cts +54 -0
- package/dist/cjs/implementations/subscriptions/subscription-plan-validators.cjs +8 -0
- package/dist/cjs/implementations/subscriptions/subscription-plan-validators.d.cts +8 -3
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/types/enyo-authentication.d.cts +11 -0
- package/dist/cjs/types/enyo-heatpump-appliance.cjs +33 -1
- package/dist/cjs/types/enyo-heatpump-appliance.d.cts +86 -0
- package/dist/cjs/types/enyo-settings.d.cts +22 -1
- package/dist/cjs/types/enyo-subscription-offer.cjs +113 -0
- package/dist/cjs/types/enyo-subscription-offer.d.cts +358 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app-package-definition.d.ts +46 -1
- package/dist/implementations/heatpump/heatpump-metadata-validators.d.ts +4 -2
- package/dist/implementations/heatpump/heatpump-metadata-validators.js +80 -2
- package/dist/implementations/subscriptions/define-subscription-plan.d.ts +40 -0
- package/dist/implementations/subscriptions/define-subscription-plan.js +45 -0
- package/dist/implementations/subscriptions/subscription-offer-validators.d.ts +54 -0
- package/dist/implementations/subscriptions/subscription-offer-validators.js +298 -0
- package/dist/implementations/subscriptions/subscription-plan-validators.d.ts +8 -3
- package/dist/implementations/subscriptions/subscription-plan-validators.js +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/types/enyo-authentication.d.ts +11 -0
- package/dist/types/enyo-heatpump-appliance.d.ts +86 -0
- package/dist/types/enyo-heatpump-appliance.js +32 -0
- package/dist/types/enyo-settings.d.ts +22 -1
- package/dist/types/enyo-subscription-offer.d.ts +358 -0
- package/dist/types/enyo-subscription-offer.js +110 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Client-side validator for how a package promotes its subscription plans:
|
|
4
|
+
* the feature catalog ({@link EnergyAppPackageDefinition.subscriptionFeatures}),
|
|
5
|
+
* the free tier ({@link EnergyAppPackageDefinition.subscriptionFreeTier}), the
|
|
6
|
+
* offer screen blocks ({@link EnergyAppPackageDefinition.subscriptionOfferScreen})
|
|
7
|
+
* and the plans' `includedFeatures` and `highlight`.
|
|
8
|
+
*
|
|
9
|
+
* `validatePackageSubscriptionPlans()` runs these checks as part of the plan
|
|
10
|
+
* validation, so most callers never call {@link validatePackageSubscriptionOffer}
|
|
11
|
+
* directly.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.validatePackageSubscriptionOffer = validatePackageSubscriptionOffer;
|
|
15
|
+
const enyo_subscription_js_1 = require("../../types/enyo-subscription.cjs");
|
|
16
|
+
const enyo_subscription_offer_js_1 = require("../../types/enyo-subscription-offer.cjs");
|
|
17
|
+
const define_subscription_plan_js_1 = require("./define-subscription-plan.cjs");
|
|
18
|
+
/** A feature key — the same kebab-case slug shape as a plan key. */
|
|
19
|
+
const SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
20
|
+
/** Languages every text on the offer screen must be translated into. */
|
|
21
|
+
const REQUIRED_LANGUAGES = ['de', 'en'];
|
|
22
|
+
/**
|
|
23
|
+
* Checks that a list of localized entries has no duplicate language, covers
|
|
24
|
+
* every required language, and fills the required fields in every entry.
|
|
25
|
+
*
|
|
26
|
+
* @param entries - The localized entries, e.g. a feature's `storeEntry`.
|
|
27
|
+
* @param at - Location prefix for messages.
|
|
28
|
+
* @param requiredFields - Fields that must be non-empty in every entry.
|
|
29
|
+
* @param errors - Collector for blocking errors.
|
|
30
|
+
*/
|
|
31
|
+
function validateTranslations(entries, at, requiredFields, errors) {
|
|
32
|
+
const languages = new Set();
|
|
33
|
+
for (const [index, entry] of (entries ?? []).entries()) {
|
|
34
|
+
const entryAt = `${at}[${index}] (${entry.language || '?'})`;
|
|
35
|
+
if (languages.has(entry.language)) {
|
|
36
|
+
errors.push(`${entryAt}: duplicate language "${entry.language}".`);
|
|
37
|
+
}
|
|
38
|
+
languages.add(entry.language);
|
|
39
|
+
for (const field of requiredFields) {
|
|
40
|
+
const value = entry[field];
|
|
41
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
42
|
+
errors.push(`${entryAt}: ${field} is required.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const language of REQUIRED_LANGUAGES) {
|
|
47
|
+
if (!languages.has(language)) {
|
|
48
|
+
errors.push(`${at}: entry for language "${language}" is required.`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Checks the included features of a plan or of the free tier: every entry
|
|
54
|
+
* names a declared feature, no feature is listed twice, and every value is
|
|
55
|
+
* translated.
|
|
56
|
+
*
|
|
57
|
+
* @param included - The included features, or `undefined` when none are declared.
|
|
58
|
+
* @param featureKeys - The keys of the feature catalog.
|
|
59
|
+
* @param at - Location prefix for messages.
|
|
60
|
+
* @param errors - Collector for blocking errors.
|
|
61
|
+
*/
|
|
62
|
+
function validateIncludedFeatures(included, featureKeys, at, errors) {
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
for (const [index, feature] of (included ?? []).entries()) {
|
|
65
|
+
const key = (0, define_subscription_plan_js_1.getIncludedFeatureKey)(feature);
|
|
66
|
+
const featureAt = `${at}.includedFeatures[${index}] (${key || '?'})`;
|
|
67
|
+
if (!featureKeys.has(key)) {
|
|
68
|
+
errors.push(`${featureAt}: "${key}" is not a feature of subscriptionFeatures.`);
|
|
69
|
+
}
|
|
70
|
+
else if (seen.has(key)) {
|
|
71
|
+
errors.push(`${featureAt}: feature "${key}" is listed twice.`);
|
|
72
|
+
}
|
|
73
|
+
seen.add(key);
|
|
74
|
+
if (typeof feature !== 'string') {
|
|
75
|
+
validateTranslations(feature.value, `${featureAt}.value`, ['value'], errors);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Checks that every key of a block's key list is declared.
|
|
81
|
+
*
|
|
82
|
+
* @param keys - The keys the block refers to, or `undefined` for "all".
|
|
83
|
+
* @param known - The declared keys.
|
|
84
|
+
* @param kind - `'feature'` or `'plan'`, used in messages.
|
|
85
|
+
* @param at - Location prefix for messages.
|
|
86
|
+
* @param errors - Collector for blocking errors.
|
|
87
|
+
*/
|
|
88
|
+
function validateBlockKeys(keys, known, kind, at, errors) {
|
|
89
|
+
if (keys === undefined)
|
|
90
|
+
return;
|
|
91
|
+
if (keys.length === 0) {
|
|
92
|
+
errors.push(`${at}: ${kind}Keys must not be empty — omit it to use every ${kind}.`);
|
|
93
|
+
}
|
|
94
|
+
const seen = new Set();
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
if (!known.has(key))
|
|
97
|
+
errors.push(`${at}: "${key}" is not a declared ${kind}.`);
|
|
98
|
+
else if (seen.has(key))
|
|
99
|
+
errors.push(`${at}: ${kind} "${key}" is listed twice.`);
|
|
100
|
+
seen.add(key);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Checks a block's optional heading and text: when set, they must be
|
|
105
|
+
* translated into every required language.
|
|
106
|
+
*
|
|
107
|
+
* @param storeEntry - The block's localized texts.
|
|
108
|
+
* @param at - Location prefix for messages.
|
|
109
|
+
* @param requiredFields - Fields that must be non-empty in every entry.
|
|
110
|
+
* @param errors - Collector for blocking errors.
|
|
111
|
+
*/
|
|
112
|
+
function validateBlockTexts(storeEntry, at, requiredFields, errors) {
|
|
113
|
+
validateTranslations(storeEntry, `${at}.storeEntry`, requiredFields, errors);
|
|
114
|
+
for (const [index, entry] of (storeEntry ?? []).entries()) {
|
|
115
|
+
if (!entry.title?.trim() && !entry.text?.trim()) {
|
|
116
|
+
errors.push(`${at}.storeEntry[${index}] (${entry.language || '?'}): title or text is required.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Checks an optional icon against {@link EnyoSubscriptionOfferIconEnum}.
|
|
122
|
+
*
|
|
123
|
+
* @param icon - The icon, or `undefined`.
|
|
124
|
+
* @param at - Location prefix for messages.
|
|
125
|
+
* @param errors - Collector for blocking errors.
|
|
126
|
+
*/
|
|
127
|
+
function validateIcon(icon, at, errors) {
|
|
128
|
+
if (icon !== undefined && !Object.values(enyo_subscription_offer_js_1.EnyoSubscriptionOfferIconEnum).includes(icon)) {
|
|
129
|
+
errors.push(`${at}: icon "${icon}" is not supported — use one of EnyoSubscriptionOfferIconEnum.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validates how a package promotes its plans:
|
|
134
|
+
*
|
|
135
|
+
* - **Features**: keys are unique kebab-case slugs, icons are known, and every
|
|
136
|
+
* feature has a German and English title.
|
|
137
|
+
* - **Plans**: `includedFeatures` name declared features, at most once each,
|
|
138
|
+
* with translated values; `highlight` is known and set on at most one plan.
|
|
139
|
+
* - **Free tier**: only with `subscriptionMode: 'optional'`, translated, and
|
|
140
|
+
* its `includedFeatures` follow the plan rules.
|
|
141
|
+
* - **Offer screen**: at least one block, every block type is known, texts are
|
|
142
|
+
* translated (a `hero` needs a title, a `hint` a known kind and a text),
|
|
143
|
+
* referenced features and plans exist, a comparison has at least two
|
|
144
|
+
* columns and a free column only with a free tier, and there is at most one
|
|
145
|
+
* `plans` block.
|
|
146
|
+
*
|
|
147
|
+
* Warnings flag features no tier includes, comparison rows no column
|
|
148
|
+
* includes, and offer declarations without plans.
|
|
149
|
+
*
|
|
150
|
+
* @param definition - The package definition, or just the fields listed in
|
|
151
|
+
* {@link PackageSubscriptionOfferSource}.
|
|
152
|
+
* @returns The {@link PackageSubscriptionOfferValidationResult}.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* const {ok, errors} = validatePackageSubscriptionOffer(packageDef);
|
|
157
|
+
* if (!ok) throw new Error(errors.join('\n'));
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
function validatePackageSubscriptionOffer(definition) {
|
|
161
|
+
const errors = [];
|
|
162
|
+
const warnings = [];
|
|
163
|
+
const plans = definition.subscriptionPlans ?? [];
|
|
164
|
+
const features = definition.subscriptionFeatures ?? [];
|
|
165
|
+
const freeTier = definition.subscriptionFreeTier;
|
|
166
|
+
const screen = definition.subscriptionOfferScreen;
|
|
167
|
+
if (!plans.length) {
|
|
168
|
+
if (definition.subscriptionFeatures || freeTier || screen) {
|
|
169
|
+
warnings.push('subscriptionFeatures, subscriptionFreeTier or subscriptionOfferScreen is set but no subscriptionPlans are declared — it has no effect.');
|
|
170
|
+
}
|
|
171
|
+
return { ok: true, errors, warnings };
|
|
172
|
+
}
|
|
173
|
+
const featureKeys = new Set();
|
|
174
|
+
for (const [index, feature] of features.entries()) {
|
|
175
|
+
const at = `subscriptionFeatures[${index}] (${feature.featureKey || '?'})`;
|
|
176
|
+
if (!feature.featureKey?.trim()) {
|
|
177
|
+
errors.push(`${at}: featureKey is required.`);
|
|
178
|
+
}
|
|
179
|
+
else if (!SLUG_RE.test(feature.featureKey)) {
|
|
180
|
+
errors.push(`${at}: featureKey must be a kebab-case slug, e.g. "price-optimisation".`);
|
|
181
|
+
}
|
|
182
|
+
else if (featureKeys.has(feature.featureKey)) {
|
|
183
|
+
errors.push(`${at}: duplicate featureKey "${feature.featureKey}" — feature keys must be unique within the package.`);
|
|
184
|
+
}
|
|
185
|
+
featureKeys.add(feature.featureKey);
|
|
186
|
+
validateIcon(feature.icon, at, errors);
|
|
187
|
+
validateTranslations(feature.storeEntry, `${at}.storeEntry`, ['title'], errors);
|
|
188
|
+
}
|
|
189
|
+
const includedSomewhere = new Set();
|
|
190
|
+
const highlighted = [];
|
|
191
|
+
for (const [index, plan] of plans.entries()) {
|
|
192
|
+
const at = `subscriptionPlans[${index}] (${plan.planKey || '?'})`;
|
|
193
|
+
validateIncludedFeatures(plan.includedFeatures, featureKeys, at, errors);
|
|
194
|
+
plan.includedFeatures?.forEach(feature => includedSomewhere.add((0, define_subscription_plan_js_1.getIncludedFeatureKey)(feature)));
|
|
195
|
+
if (plan.highlight !== undefined) {
|
|
196
|
+
if (!Object.values(enyo_subscription_offer_js_1.EnyoSubscriptionPlanHighlightEnum).includes(plan.highlight)) {
|
|
197
|
+
errors.push(`${at}: highlight "${plan.highlight}" is not supported.`);
|
|
198
|
+
}
|
|
199
|
+
highlighted.push(plan.planKey);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (highlighted.length > 1) {
|
|
203
|
+
errors.push(`Only one plan may be highlighted, but ${highlighted.map(key => `"${key}"`).join(', ')} are.`);
|
|
204
|
+
}
|
|
205
|
+
if (freeTier) {
|
|
206
|
+
if (definition.subscriptionMode === enyo_subscription_js_1.EnyoSubscriptionModeEnum.Required) {
|
|
207
|
+
errors.push('subscriptionFreeTier is not allowed with subscriptionMode "required" — the app has no free base.');
|
|
208
|
+
}
|
|
209
|
+
validateTranslations(freeTier.storeEntry, 'subscriptionFreeTier.storeEntry', ['title'], errors);
|
|
210
|
+
validateIncludedFeatures(freeTier.includedFeatures, featureKeys, 'subscriptionFreeTier', errors);
|
|
211
|
+
freeTier.includedFeatures?.forEach(feature => includedSomewhere.add((0, define_subscription_plan_js_1.getIncludedFeatureKey)(feature)));
|
|
212
|
+
}
|
|
213
|
+
for (const feature of features) {
|
|
214
|
+
if (feature.featureKey && !includedSomewhere.has(feature.featureKey)) {
|
|
215
|
+
warnings.push(`Feature "${feature.featureKey}" is not included in any plan or the free tier.`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (screen) {
|
|
219
|
+
validateOfferScreenBlocks(screen.blocks, definition, featureKeys, errors, warnings);
|
|
220
|
+
}
|
|
221
|
+
return { ok: errors.length === 0, errors, warnings };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Checks the blocks of the offer screen.
|
|
225
|
+
*
|
|
226
|
+
* @param blocks - The declared blocks.
|
|
227
|
+
* @param definition - The package definition, for plans and the free tier.
|
|
228
|
+
* @param featureKeys - The keys of the feature catalog.
|
|
229
|
+
* @param errors - Collector for blocking errors.
|
|
230
|
+
* @param warnings - Collector for advisory problems.
|
|
231
|
+
*/
|
|
232
|
+
function validateOfferScreenBlocks(blocks, definition, featureKeys, errors, warnings) {
|
|
233
|
+
if (!Array.isArray(blocks) || blocks.length === 0) {
|
|
234
|
+
errors.push('subscriptionOfferScreen.blocks must contain at least one block — omit subscriptionOfferScreen to show only the plans.');
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const plans = definition.subscriptionPlans ?? [];
|
|
238
|
+
const planKeys = new Set(plans.map(plan => plan.planKey));
|
|
239
|
+
let plansBlocks = 0;
|
|
240
|
+
for (const [index, block] of blocks.entries()) {
|
|
241
|
+
const at = `subscriptionOfferScreen.blocks[${index}] (${block?.type || '?'})`;
|
|
242
|
+
switch (block?.type) {
|
|
243
|
+
case enyo_subscription_offer_js_1.EnyoSubscriptionOfferBlockTypeEnum.Hero:
|
|
244
|
+
validateIcon(block.icon, at, errors);
|
|
245
|
+
validateBlockTexts(block.storeEntry, at, ['title'], errors);
|
|
246
|
+
break;
|
|
247
|
+
case enyo_subscription_offer_js_1.EnyoSubscriptionOfferBlockTypeEnum.Features:
|
|
248
|
+
if (featureKeys.size === 0) {
|
|
249
|
+
errors.push(`${at}: a features block needs subscriptionFeatures to list.`);
|
|
250
|
+
}
|
|
251
|
+
validateBlockKeys(block.featureKeys, featureKeys, 'feature', at, errors);
|
|
252
|
+
if (block.storeEntry)
|
|
253
|
+
validateBlockTexts(block.storeEntry, at, [], errors);
|
|
254
|
+
break;
|
|
255
|
+
case enyo_subscription_offer_js_1.EnyoSubscriptionOfferBlockTypeEnum.Comparison: {
|
|
256
|
+
if (featureKeys.size === 0) {
|
|
257
|
+
errors.push(`${at}: a comparison block needs subscriptionFeatures as its rows.`);
|
|
258
|
+
}
|
|
259
|
+
validateBlockKeys(block.featureKeys, featureKeys, 'feature', at, errors);
|
|
260
|
+
validateBlockKeys(block.planKeys, planKeys, 'plan', at, errors);
|
|
261
|
+
const freeColumn = block.includeFreeTier ?? definition.subscriptionFreeTier !== undefined;
|
|
262
|
+
if (freeColumn && !definition.subscriptionFreeTier) {
|
|
263
|
+
errors.push(`${at}: includeFreeTier needs subscriptionFreeTier to fill the free column.`);
|
|
264
|
+
}
|
|
265
|
+
const columnPlans = plans.filter(plan => block.planKeys?.includes(plan.planKey) ?? true);
|
|
266
|
+
if (columnPlans.length + (freeColumn ? 1 : 0) < 2) {
|
|
267
|
+
errors.push(`${at}: a comparison needs at least two columns — add the free tier or another plan.`);
|
|
268
|
+
}
|
|
269
|
+
const included = new Set([
|
|
270
|
+
...(freeColumn ? definition.subscriptionFreeTier?.includedFeatures ?? [] : []),
|
|
271
|
+
...columnPlans.flatMap(plan => plan.includedFeatures ?? []),
|
|
272
|
+
].map(define_subscription_plan_js_1.getIncludedFeatureKey));
|
|
273
|
+
for (const key of block.featureKeys ?? featureKeys) {
|
|
274
|
+
if (featureKeys.has(key) && !included.has(key)) {
|
|
275
|
+
warnings.push(`${at}: feature "${key}" is not included in any compared column — its row stays empty.`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (block.storeEntry)
|
|
279
|
+
validateBlockTexts(block.storeEntry, at, [], errors);
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
case enyo_subscription_offer_js_1.EnyoSubscriptionOfferBlockTypeEnum.Hint:
|
|
283
|
+
if (!Object.values(enyo_subscription_offer_js_1.EnyoSubscriptionOfferHintKindEnum).includes(block.kind)) {
|
|
284
|
+
errors.push(`${at}: kind "${block.kind}" is not supported.`);
|
|
285
|
+
}
|
|
286
|
+
validateIcon(block.icon, at, errors);
|
|
287
|
+
validateBlockTexts(block.storeEntry, at, ['text'], errors);
|
|
288
|
+
break;
|
|
289
|
+
case enyo_subscription_offer_js_1.EnyoSubscriptionOfferBlockTypeEnum.Plans:
|
|
290
|
+
plansBlocks++;
|
|
291
|
+
if (block.storeEntry)
|
|
292
|
+
validateBlockTexts(block.storeEntry, at, [], errors);
|
|
293
|
+
break;
|
|
294
|
+
default:
|
|
295
|
+
errors.push(`${at}: block type "${block?.type}" is not supported.`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (plansBlocks > 1) {
|
|
299
|
+
errors.push('subscriptionOfferScreen.blocks may contain at most one "plans" block.');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side validator for how a package promotes its subscription plans:
|
|
3
|
+
* the feature catalog ({@link EnergyAppPackageDefinition.subscriptionFeatures}),
|
|
4
|
+
* the free tier ({@link EnergyAppPackageDefinition.subscriptionFreeTier}), the
|
|
5
|
+
* offer screen blocks ({@link EnergyAppPackageDefinition.subscriptionOfferScreen})
|
|
6
|
+
* and the plans' `includedFeatures` and `highlight`.
|
|
7
|
+
*
|
|
8
|
+
* `validatePackageSubscriptionPlans()` runs these checks as part of the plan
|
|
9
|
+
* validation, so most callers never call {@link validatePackageSubscriptionOffer}
|
|
10
|
+
* directly.
|
|
11
|
+
*/
|
|
12
|
+
import type { EnergyAppPackageDefinition } from '../../energy-app-package-definition.cjs';
|
|
13
|
+
/** The outcome of validating how a package promotes its plans. */
|
|
14
|
+
export interface PackageSubscriptionOfferValidationResult {
|
|
15
|
+
/** True when there are no blocking `errors` (warnings are still allowed). */
|
|
16
|
+
ok: boolean;
|
|
17
|
+
/** Blocking problems — these must be fixed before releasing. */
|
|
18
|
+
errors: string[];
|
|
19
|
+
/** Advisory problems — allowed, but usually worth reviewing. */
|
|
20
|
+
warnings: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The parts of a package definition the offer validator reads.
|
|
24
|
+
*/
|
|
25
|
+
export type PackageSubscriptionOfferSource = Pick<EnergyAppPackageDefinition, 'subscriptionPlans' | 'subscriptionMode' | 'subscriptionFeatures' | 'subscriptionFreeTier' | 'subscriptionOfferScreen'>;
|
|
26
|
+
/**
|
|
27
|
+
* Validates how a package promotes its plans:
|
|
28
|
+
*
|
|
29
|
+
* - **Features**: keys are unique kebab-case slugs, icons are known, and every
|
|
30
|
+
* feature has a German and English title.
|
|
31
|
+
* - **Plans**: `includedFeatures` name declared features, at most once each,
|
|
32
|
+
* with translated values; `highlight` is known and set on at most one plan.
|
|
33
|
+
* - **Free tier**: only with `subscriptionMode: 'optional'`, translated, and
|
|
34
|
+
* its `includedFeatures` follow the plan rules.
|
|
35
|
+
* - **Offer screen**: at least one block, every block type is known, texts are
|
|
36
|
+
* translated (a `hero` needs a title, a `hint` a known kind and a text),
|
|
37
|
+
* referenced features and plans exist, a comparison has at least two
|
|
38
|
+
* columns and a free column only with a free tier, and there is at most one
|
|
39
|
+
* `plans` block.
|
|
40
|
+
*
|
|
41
|
+
* Warnings flag features no tier includes, comparison rows no column
|
|
42
|
+
* includes, and offer declarations without plans.
|
|
43
|
+
*
|
|
44
|
+
* @param definition - The package definition, or just the fields listed in
|
|
45
|
+
* {@link PackageSubscriptionOfferSource}.
|
|
46
|
+
* @returns The {@link PackageSubscriptionOfferValidationResult}.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const {ok, errors} = validatePackageSubscriptionOffer(packageDef);
|
|
51
|
+
* if (!ok) throw new Error(errors.join('\n'));
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function validatePackageSubscriptionOffer(definition: PackageSubscriptionOfferSource): PackageSubscriptionOfferValidationResult;
|
|
@@ -22,6 +22,7 @@ const energy_app_permission_type_js_1 = require("../../energy-app-permission.typ
|
|
|
22
22
|
const enyo_energy_app_sdk_js_1 = require("../../enyo-energy-app-sdk.cjs");
|
|
23
23
|
const enyo_currency_js_1 = require("../../types/enyo-currency.cjs");
|
|
24
24
|
const enyo_subscription_js_1 = require("../../types/enyo-subscription.cjs");
|
|
25
|
+
const subscription_offer_validators_js_1 = require("./subscription-offer-validators.cjs");
|
|
25
26
|
/**
|
|
26
27
|
* Thrown by {@link assertValidPackageSubscriptionPlans} when a package's
|
|
27
28
|
* subscription plans fail validation. The message lists every blocking error
|
|
@@ -128,6 +129,10 @@ function validateStoreEntries(plan, at, errors) {
|
|
|
128
129
|
*
|
|
129
130
|
* A `subscriptionMode` without plans is a warning: it has no effect.
|
|
130
131
|
*
|
|
132
|
+
* Also runs `validatePackageSubscriptionOffer()` on the feature catalog, the
|
|
133
|
+
* free tier, the offer screen and the plans' `includedFeatures` and
|
|
134
|
+
* `highlight`, and merges its errors and warnings into the result.
|
|
135
|
+
*
|
|
131
136
|
* @param definition - The package definition, or just its `subscriptionPlans`,
|
|
132
137
|
* `subscriptionMode` and `permissions`. A package without plans is valid.
|
|
133
138
|
* @returns The {@link PackageSubscriptionPlansValidationResult}.
|
|
@@ -143,6 +148,8 @@ function validatePackageSubscriptionPlans(definition) {
|
|
|
143
148
|
const warnings = [];
|
|
144
149
|
const plans = definition.subscriptionPlans;
|
|
145
150
|
const mode = definition.subscriptionMode;
|
|
151
|
+
const offer = (0, subscription_offer_validators_js_1.validatePackageSubscriptionOffer)(definition);
|
|
152
|
+
warnings.push(...offer.warnings);
|
|
146
153
|
if (!plans?.length) {
|
|
147
154
|
if (mode !== undefined) {
|
|
148
155
|
warnings.push('subscriptionMode is set but no subscriptionPlans are declared — it has no effect.');
|
|
@@ -192,6 +199,7 @@ function validatePackageSubscriptionPlans(definition) {
|
|
|
192
199
|
}
|
|
193
200
|
validateStoreEntries(plan, at, errors);
|
|
194
201
|
}
|
|
202
|
+
errors.push(...offer.errors);
|
|
195
203
|
return { ok: errors.length === 0, errors, warnings };
|
|
196
204
|
}
|
|
197
205
|
/**
|
|
@@ -37,10 +37,11 @@ export interface PackageSubscriptionPlansValidationResult {
|
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* The parts of a package definition the validator reads: the plans, the
|
|
40
|
-
* subscription mode,
|
|
41
|
-
* subscriptions
|
|
40
|
+
* subscription mode, the permissions to check that the app can read its
|
|
41
|
+
* subscriptions, and optionally how the plans are promoted (features, free
|
|
42
|
+
* tier and offer screen).
|
|
42
43
|
*/
|
|
43
|
-
export type PackageSubscriptionPlansSource = Pick<EnergyAppPackageDefinition, 'subscriptionPlans' | 'permissions' | 'subscriptionMode'
|
|
44
|
+
export type PackageSubscriptionPlansSource = Pick<EnergyAppPackageDefinition, 'subscriptionPlans' | 'permissions' | 'subscriptionMode'> & Partial<Pick<EnergyAppPackageDefinition, 'subscriptionFeatures' | 'subscriptionFreeTier' | 'subscriptionOfferScreen'>>;
|
|
44
45
|
/**
|
|
45
46
|
* Structural validation of a package's subscription plans: plan keys are
|
|
46
47
|
* unique kebab-case slugs, the payment interval is known, every plan has at
|
|
@@ -52,6 +53,10 @@ export type PackageSubscriptionPlansSource = Pick<EnergyAppPackageDefinition, 's
|
|
|
52
53
|
*
|
|
53
54
|
* A `subscriptionMode` without plans is a warning: it has no effect.
|
|
54
55
|
*
|
|
56
|
+
* Also runs `validatePackageSubscriptionOffer()` on the feature catalog, the
|
|
57
|
+
* free tier, the offer screen and the plans' `includedFeatures` and
|
|
58
|
+
* `highlight`, and merges its errors and warnings into the result.
|
|
59
|
+
*
|
|
55
60
|
* @param definition - The package definition, or just its `subscriptionPlans`,
|
|
56
61
|
* `subscriptionMode` and `permissions`. A package without plans is valid.
|
|
57
62
|
* @returns The {@link PackageSubscriptionPlansValidationResult}.
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -193,6 +193,7 @@ __exportStar(require("./packages/energy-app-cascade.cjs"), exports);
|
|
|
193
193
|
__exportStar(require("./types/enyo-webhook.cjs"), exports);
|
|
194
194
|
__exportStar(require("./packages/energy-app-webhook.cjs"), exports);
|
|
195
195
|
__exportStar(require("./types/enyo-subscription.cjs"), exports);
|
|
196
|
+
__exportStar(require("./types/enyo-subscription-offer.cjs"), exports);
|
|
196
197
|
__exportStar(require("./packages/energy-app-subscriptions.cjs"), exports);
|
|
197
198
|
__exportStar(require("./types/enyo-room.cjs"), exports);
|
|
198
199
|
__exportStar(require("./packages/energy-app-room.cjs"), exports);
|
|
@@ -200,6 +201,7 @@ __exportStar(require("./types/enyo-room-temperature.cjs"), exports);
|
|
|
200
201
|
__exportStar(require("./packages/energy-app-room-temperatures.cjs"), exports);
|
|
201
202
|
__exportStar(require("./implementations/subscriptions/define-subscription-plan.cjs"), exports);
|
|
202
203
|
__exportStar(require("./implementations/subscriptions/subscription-plan-validators.cjs"), exports);
|
|
204
|
+
__exportStar(require("./implementations/subscriptions/subscription-offer-validators.cjs"), exports);
|
|
203
205
|
__exportStar(require("./implementations/subscriptions/in-memory-subscriptions.cjs"), exports);
|
|
204
206
|
__exportStar(require("./implementations/pricing/price-schedule-resolver.cjs"), exports);
|
|
205
207
|
__exportStar(require("./implementations/pricing/price-component-validators.cjs"), exports);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -177,6 +177,7 @@ export * from './packages/energy-app-cascade.cjs';
|
|
|
177
177
|
export * from './types/enyo-webhook.cjs';
|
|
178
178
|
export * from './packages/energy-app-webhook.cjs';
|
|
179
179
|
export * from './types/enyo-subscription.cjs';
|
|
180
|
+
export * from './types/enyo-subscription-offer.cjs';
|
|
180
181
|
export * from './packages/energy-app-subscriptions.cjs';
|
|
181
182
|
export * from './types/enyo-room.cjs';
|
|
182
183
|
export * from './packages/energy-app-room.cjs';
|
|
@@ -184,6 +185,7 @@ export * from './types/enyo-room-temperature.cjs';
|
|
|
184
185
|
export * from './packages/energy-app-room-temperatures.cjs';
|
|
185
186
|
export * from './implementations/subscriptions/define-subscription-plan.cjs';
|
|
186
187
|
export * from './implementations/subscriptions/subscription-plan-validators.cjs';
|
|
188
|
+
export * from './implementations/subscriptions/subscription-offer-validators.cjs';
|
|
187
189
|
export * from './implementations/subscriptions/in-memory-subscriptions.cjs';
|
|
188
190
|
export * from './implementations/pricing/price-schedule-resolver.cjs';
|
|
189
191
|
export * from './implementations/pricing/price-component-validators.cjs';
|
|
@@ -174,6 +174,17 @@ export interface EnyoAuthentication {
|
|
|
174
174
|
* presented before or after the onboarding guide. When omitted, the host decides the default.
|
|
175
175
|
*/
|
|
176
176
|
onboardingOrder?: EnyoAuthenticationOnboardingOrder;
|
|
177
|
+
/**
|
|
178
|
+
* Forces the host to show this authentication step even when the package is already in
|
|
179
|
+
* {@link EnyoAuthenticationStateEnum.Authenticated} state.
|
|
180
|
+
*
|
|
181
|
+
* By default the host skips the authentication step if the package reports it is already
|
|
182
|
+
* authenticated. Set this to `true` when the user must authenticate again regardless of the
|
|
183
|
+
* current state — for example to re-link an account, grant additional scopes, or authenticate
|
|
184
|
+
* a newly added appliance against the same vendor account. When omitted or `false`, the
|
|
185
|
+
* host keeps its default behaviour and may skip the step.
|
|
186
|
+
*/
|
|
187
|
+
authenticationRequired?: boolean;
|
|
177
188
|
}
|
|
178
189
|
export interface EnyoApiKeyAuthenticationResponse {
|
|
179
190
|
apiKey: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HEATPUMP_CURVE_OPTIMIZATION_DEFAULT_TARGET_C = exports.HEATPUMP_CURVE_OPTIMIZATION_MAX_LOWERING_K = exports.EnyoHeatpumpHeatingControlReferenceEnum = exports.EnyoHeatpumpRoomTemperatureSourceEnum = exports.EnyoHeatpumpProgramModeEnum = exports.EnyoHeatpumpTimeProgramLevelEnum = exports.EnyoHeatpumpApplianceHeatingCircuitTypeEnum = exports.EnyoHeatpumpApplianceStorageTypeEnum = exports.EnyoHeatpumpApplianceHeatingRodUsageEnum = exports.EnyoHeatpumpApplianceAdditionalDeviceEnum = exports.EnyoHeatpumpApplianceConnectionTypeEnum = exports.EnyoHeatpumpApplianceModeEnum = exports.EnyoHeatpumpApplianceAvailableFeaturesEnum = void 0;
|
|
3
|
+
exports.HEATPUMP_CURVE_OPTIMIZATION_DEFAULT_TARGET_C = exports.HEATPUMP_CURVE_OPTIMIZATION_MAX_LOWERING_K = exports.EnyoHeatpumpHeatingControlReferenceEnum = exports.EnyoHeatpumpRoomTemperatureSourceEnum = exports.EnyoHeatpumpProgramModeEnum = exports.EnyoHeatpumpTimeProgramLevelEnum = exports.EnyoHeatpumpApplianceHeatingCircuitTypeEnum = exports.EnyoHeatpumpApplianceStorageTypeEnum = exports.EnyoHeatpumpApplianceHeatingRodUsageEnum = exports.EnyoHeatpumpApplianceAdditionalDeviceEnum = exports.EnyoHeatpumpSgReadyStateEnum = exports.EnyoHeatpumpSgReadyFunctionEnum = exports.EnyoHeatpumpApplianceConnectionTypeEnum = exports.EnyoHeatpumpApplianceModeEnum = exports.EnyoHeatpumpApplianceAvailableFeaturesEnum = void 0;
|
|
4
4
|
var EnyoHeatpumpApplianceAvailableFeaturesEnum;
|
|
5
5
|
(function (EnyoHeatpumpApplianceAvailableFeaturesEnum) {
|
|
6
6
|
/** If the heatpump is capable of domestic hot water*/
|
|
@@ -156,6 +156,38 @@ var EnyoHeatpumpApplianceConnectionTypeEnum;
|
|
|
156
156
|
/** Connected via a vendor or local API (e.g. REST, Modbus, EEBus) */
|
|
157
157
|
EnyoHeatpumpApplianceConnectionTypeEnum["Api"] = "api";
|
|
158
158
|
})(EnyoHeatpumpApplianceConnectionTypeEnum || (exports.EnyoHeatpumpApplianceConnectionTypeEnum = EnyoHeatpumpApplianceConnectionTypeEnum = {}));
|
|
159
|
+
/**
|
|
160
|
+
* What an SG Ready relay of a heatpump installation acts on
|
|
161
|
+
* ({@link EnyoHeatpumpApplianceSgReadyRelay.function}).
|
|
162
|
+
*
|
|
163
|
+
* Installations often wire more than one SG Ready input — e.g. one for space
|
|
164
|
+
* heating and one for domestic hot water — so the energy manager can raise one
|
|
165
|
+
* without the other.
|
|
166
|
+
*/
|
|
167
|
+
var EnyoHeatpumpSgReadyFunctionEnum;
|
|
168
|
+
(function (EnyoHeatpumpSgReadyFunctionEnum) {
|
|
169
|
+
/** Heats domestic hot water with the electric heating rod of the DHW tank. */
|
|
170
|
+
EnyoHeatpumpSgReadyFunctionEnum["DhwHeatingRod"] = "dhw-heating-rod";
|
|
171
|
+
/** Heats domestic hot water with the heatpump's compressor. */
|
|
172
|
+
EnyoHeatpumpSgReadyFunctionEnum["DhwHeatpump"] = "dhw-heatpump";
|
|
173
|
+
/** Space heating of a heating circuit. */
|
|
174
|
+
EnyoHeatpumpSgReadyFunctionEnum["Heating"] = "heating";
|
|
175
|
+
})(EnyoHeatpumpSgReadyFunctionEnum || (exports.EnyoHeatpumpSgReadyFunctionEnum = EnyoHeatpumpSgReadyFunctionEnum = {}));
|
|
176
|
+
/**
|
|
177
|
+
* The four operating states of an SG Ready input, numbered 1–4 by the SG Ready
|
|
178
|
+
* label of the Bundesverband Wärmepumpe.
|
|
179
|
+
*/
|
|
180
|
+
var EnyoHeatpumpSgReadyStateEnum;
|
|
181
|
+
(function (EnyoHeatpumpSgReadyStateEnum) {
|
|
182
|
+
/** State 1 (contacts 1:0): operation blocked, e.g. by the grid operator. */
|
|
183
|
+
EnyoHeatpumpSgReadyStateEnum["Blocked"] = "blocked";
|
|
184
|
+
/** State 2 (contacts 0:0): normal operation; the heatpump follows its own control. */
|
|
185
|
+
EnyoHeatpumpSgReadyStateEnum["Normal"] = "normal";
|
|
186
|
+
/** State 3 (contacts 0:1): switch-on recommended; the heatpump raises its setpoints. */
|
|
187
|
+
EnyoHeatpumpSgReadyStateEnum["Recommended"] = "recommended";
|
|
188
|
+
/** State 4 (contacts 1:1): switch-on forced; the heatpump runs at its maximum setpoints. */
|
|
189
|
+
EnyoHeatpumpSgReadyStateEnum["Forced"] = "forced";
|
|
190
|
+
})(EnyoHeatpumpSgReadyStateEnum || (exports.EnyoHeatpumpSgReadyStateEnum = EnyoHeatpumpSgReadyStateEnum = {}));
|
|
159
191
|
/**
|
|
160
192
|
* Additional heating devices that can be attached to / combined with a
|
|
161
193
|
* heatpump installation.
|
|
@@ -150,6 +150,82 @@ export declare enum EnyoHeatpumpApplianceConnectionTypeEnum {
|
|
|
150
150
|
/** Connected via a vendor or local API (e.g. REST, Modbus, EEBus) */
|
|
151
151
|
Api = "api"
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* What an SG Ready relay of a heatpump installation acts on
|
|
155
|
+
* ({@link EnyoHeatpumpApplianceSgReadyRelay.function}).
|
|
156
|
+
*
|
|
157
|
+
* Installations often wire more than one SG Ready input — e.g. one for space
|
|
158
|
+
* heating and one for domestic hot water — so the energy manager can raise one
|
|
159
|
+
* without the other.
|
|
160
|
+
*/
|
|
161
|
+
export declare enum EnyoHeatpumpSgReadyFunctionEnum {
|
|
162
|
+
/** Heats domestic hot water with the electric heating rod of the DHW tank. */
|
|
163
|
+
DhwHeatingRod = "dhw-heating-rod",
|
|
164
|
+
/** Heats domestic hot water with the heatpump's compressor. */
|
|
165
|
+
DhwHeatpump = "dhw-heatpump",
|
|
166
|
+
/** Space heating of a heating circuit. */
|
|
167
|
+
Heating = "heating"
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The four operating states of an SG Ready input, numbered 1–4 by the SG Ready
|
|
171
|
+
* label of the Bundesverband Wärmepumpe.
|
|
172
|
+
*/
|
|
173
|
+
export declare enum EnyoHeatpumpSgReadyStateEnum {
|
|
174
|
+
/** State 1 (contacts 1:0): operation blocked, e.g. by the grid operator. */
|
|
175
|
+
Blocked = "blocked",
|
|
176
|
+
/** State 2 (contacts 0:0): normal operation; the heatpump follows its own control. */
|
|
177
|
+
Normal = "normal",
|
|
178
|
+
/** State 3 (contacts 0:1): switch-on recommended; the heatpump raises its setpoints. */
|
|
179
|
+
Recommended = "recommended",
|
|
180
|
+
/** State 4 (contacts 1:1): switch-on forced; the heatpump runs at its maximum setpoints. */
|
|
181
|
+
Forced = "forced"
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* One SG Ready input of a heatpump installation — the two relay contacts that
|
|
185
|
+
* switch the four {@link EnyoHeatpumpSgReadyStateEnum | SG Ready states} — and
|
|
186
|
+
* what it acts on. Declared in {@link EnyoHeatpumpApplianceMetadata.sgReadyRelays}.
|
|
187
|
+
*
|
|
188
|
+
* An input wired with only one contact can switch only two states; declare
|
|
189
|
+
* them in {@link supportedStates}, e.g. `[Normal, Recommended]`.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```typescript
|
|
193
|
+
* sgReadyRelays: [
|
|
194
|
+
* {index: 0, function: EnyoHeatpumpSgReadyFunctionEnum.Heating, zoneIndex: 0, name: 'K1/K2'},
|
|
195
|
+
* {index: 1, function: EnyoHeatpumpSgReadyFunctionEnum.DhwHeatpump, zoneIndex: 0, name: 'K3/K4'},
|
|
196
|
+
* {
|
|
197
|
+
* index: 2,
|
|
198
|
+
* function: EnyoHeatpumpSgReadyFunctionEnum.DhwHeatingRod,
|
|
199
|
+
* supportedStates: [EnyoHeatpumpSgReadyStateEnum.Normal, EnyoHeatpumpSgReadyStateEnum.Forced]
|
|
200
|
+
* }
|
|
201
|
+
* ]
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export interface EnyoHeatpumpApplianceSgReadyRelay {
|
|
205
|
+
/**
|
|
206
|
+
* Zero-based index of this relay, unique within the heatpump. Stable for as
|
|
207
|
+
* long as the wiring stays the same, so commands and readings can refer to
|
|
208
|
+
* the relay by it.
|
|
209
|
+
*/
|
|
210
|
+
index: number;
|
|
211
|
+
/** What the relay acts on. */
|
|
212
|
+
function: EnyoHeatpumpSgReadyFunctionEnum;
|
|
213
|
+
/**
|
|
214
|
+
* The zone the relay acts on: the {@link EnyoHeatpumpApplianceHeatingCircuit.index}
|
|
215
|
+
* for {@link EnyoHeatpumpSgReadyFunctionEnum.Heating}, the
|
|
216
|
+
* {@link EnyoHeatpumpApplianceDomesticHotWater.index} for the DHW
|
|
217
|
+
* functions. Omit for the first (or only) zone.
|
|
218
|
+
*/
|
|
219
|
+
zoneIndex?: number;
|
|
220
|
+
/** Optional label for installers and the UI, e.g. the terminal names `'K1/K2'`. */
|
|
221
|
+
name?: string;
|
|
222
|
+
/**
|
|
223
|
+
* The SG Ready states this relay can switch. Omit when all four are wired.
|
|
224
|
+
*/
|
|
225
|
+
supportedStates?: EnyoHeatpumpSgReadyStateEnum[];
|
|
226
|
+
/** The state the relay is currently in, when known. Must be one of {@link supportedStates}. */
|
|
227
|
+
activeState?: EnyoHeatpumpSgReadyStateEnum;
|
|
228
|
+
}
|
|
153
229
|
/**
|
|
154
230
|
* Additional heating devices that can be attached to / combined with a
|
|
155
231
|
* heatpump installation.
|
|
@@ -810,6 +886,16 @@ export interface EnyoHeatpumpApplianceMetadata {
|
|
|
810
886
|
* states, while an API connection typically allows direct read/write.
|
|
811
887
|
*/
|
|
812
888
|
connectionType?: EnyoHeatpumpApplianceConnectionTypeEnum;
|
|
889
|
+
/**
|
|
890
|
+
* The SG Ready relays of the installation, 0 to n, each with the function
|
|
891
|
+
* it acts on (space heating, DHW by heatpump, DHW by heating rod). Omit or
|
|
892
|
+
* leave empty when no SG Ready input is wired.
|
|
893
|
+
*
|
|
894
|
+
* Independent of {@link connectionType}: an installation read through an
|
|
895
|
+
* API can still have SG Ready relays wired for control. Validate with
|
|
896
|
+
* `validateHeatpumpAdvancedMetadata()`.
|
|
897
|
+
*/
|
|
898
|
+
sgReadyRelays?: EnyoHeatpumpApplianceSgReadyRelay[];
|
|
813
899
|
/**
|
|
814
900
|
* Whether the energy manager is allowed to actively control (steer) this
|
|
815
901
|
* heatpump. When `false`, the heatpump is treated as read-only/monitor-only
|
|
@@ -83,6 +83,25 @@ export interface EnyoPackageConfigurationSettingIntegerOptions {
|
|
|
83
83
|
export interface EnyoPackageConfigurationSettingTextOptions {
|
|
84
84
|
maxLength?: number;
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Configuration options for action-type settings.
|
|
88
|
+
*
|
|
89
|
+
* An action setting renders as a button instead of an input, for a one-off command (restart a
|
|
90
|
+
* device, start a self-test). Anything with an on/off state belongs in a `checkbox` instead.
|
|
91
|
+
* It holds no meaningful value of its own: every tap writes the current time as an ISO 8601
|
|
92
|
+
* string (e.g. `"2026-10-29T10:15:00.000Z"`) to the setting, so each tap reaches
|
|
93
|
+
* {@link EnyoSettingsChangeListener} as a change event with a new value — even a second tap
|
|
94
|
+
* of the same button. Packages react to the event, not to the stored value.
|
|
95
|
+
*/
|
|
96
|
+
export interface EnyoPackageConfigurationSettingActionOptions {
|
|
97
|
+
/** Translated caption of the button (e.g. "Restart now") */
|
|
98
|
+
buttonCaption: EnyoPackageConfigurationTranslatedValue[];
|
|
99
|
+
/**
|
|
100
|
+
* Optional translated question the user has to confirm before the action is triggered.
|
|
101
|
+
* Omit it to trigger the action on the first tap.
|
|
102
|
+
*/
|
|
103
|
+
confirmationText?: EnyoPackageConfigurationTranslatedValue[];
|
|
104
|
+
}
|
|
86
105
|
/**
|
|
87
106
|
* Represents a single configuration setting for an Energy App package.
|
|
88
107
|
*/
|
|
@@ -90,7 +109,7 @@ export interface EnyoPackageConfigurationSetting {
|
|
|
90
109
|
/** internal name of the setting - must be unique */
|
|
91
110
|
name: string;
|
|
92
111
|
/** the type of the setting */
|
|
93
|
-
type: 'text' | 'select' | 'float' | 'integer' | 'checkbox' | 'time' | 'priority';
|
|
112
|
+
type: 'text' | 'select' | 'float' | 'integer' | 'checkbox' | 'time' | 'priority' | 'action';
|
|
94
113
|
/** if the setting is required */
|
|
95
114
|
required: boolean;
|
|
96
115
|
/** The displayed name of the field */
|
|
@@ -111,6 +130,8 @@ export interface EnyoPackageConfigurationSetting {
|
|
|
111
130
|
timeOptions?: EnyoPackageConfigurationSettingTimeOptions;
|
|
112
131
|
/** Optional configuration for priority-type settings, defining the available priority options with order indices */
|
|
113
132
|
priorityOptions?: EnyoPackageConfigurationSettingPriorityOption[];
|
|
133
|
+
/** Configuration for action-type settings (a button). Required when `type` is `'action'`. */
|
|
134
|
+
actionOptions?: EnyoPackageConfigurationSettingActionOptions;
|
|
114
135
|
}
|
|
115
136
|
/**
|
|
116
137
|
* Represents a change event when a setting value is modified.
|