@enyo-energy/energy-app-sdk 1.2.0 → 1.4.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 +245 -5
- package/dist/cjs/energy-app-package-definition.cjs +1 -0
- package/dist/cjs/energy-app-package-definition.d.cts +1 -0
- package/dist/cjs/energy-app-permission.type.cjs +2 -0
- package/dist/cjs/energy-app-permission.type.d.cts +3 -1
- package/dist/cjs/energy-app.cjs +22 -0
- package/dist/cjs/energy-app.d.cts +21 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
- package/dist/cjs/implementations/appliances/appliance-manager.cjs +6 -0
- package/dist/cjs/implementations/pricing/compose-electricity-prices.cjs +255 -0
- package/dist/cjs/implementations/pricing/compose-electricity-prices.d.cts +125 -0
- package/dist/cjs/implementations/pricing/price-component-validators.cjs +238 -0
- package/dist/cjs/implementations/pricing/price-component-validators.d.cts +57 -0
- package/dist/cjs/implementations/pricing/price-schedule-resolver.cjs +182 -0
- package/dist/cjs/implementations/pricing/price-schedule-resolver.d.cts +95 -0
- package/dist/cjs/index.cjs +7 -0
- package/dist/cjs/index.d.cts +7 -0
- package/dist/cjs/packages/energy-app-charge.d.cts +8 -81
- package/dist/cjs/packages/energy-app-electricity-tariff.d.cts +153 -41
- package/dist/cjs/packages/energy-app-grid-fee.cjs +2 -0
- package/dist/cjs/packages/energy-app-grid-fee.d.cts +199 -0
- package/dist/cjs/packages/energy-app-modbus.d.cts +12 -0
- package/dist/cjs/types/enyo-charge.d.cts +0 -71
- package/dist/cjs/types/enyo-data-bus-value.d.cts +7 -0
- package/dist/cjs/types/enyo-electricity-tariff.cjs +73 -13
- package/dist/cjs/types/enyo-electricity-tariff.d.cts +260 -50
- package/dist/cjs/types/enyo-energy-prices.d.cts +9 -3
- package/dist/cjs/types/enyo-grid-fee.cjs +26 -0
- package/dist/cjs/types/enyo-grid-fee.d.cts +247 -0
- package/dist/cjs/types/enyo-price-composition.cjs +15 -0
- package/dist/cjs/types/enyo-price-composition.d.cts +147 -0
- package/dist/cjs/types/enyo-price-schedule.cjs +45 -0
- package/dist/cjs/types/enyo-price-schedule.d.cts +137 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app-package-definition.d.ts +1 -0
- package/dist/energy-app-package-definition.js +1 -0
- package/dist/energy-app-permission.type.d.ts +3 -1
- package/dist/energy-app-permission.type.js +2 -0
- package/dist/energy-app.d.ts +21 -0
- package/dist/energy-app.js +22 -0
- package/dist/enyo-energy-app-sdk.d.ts +3 -0
- package/dist/implementations/appliances/appliance-manager.js +6 -0
- package/dist/implementations/pricing/compose-electricity-prices.d.ts +125 -0
- package/dist/implementations/pricing/compose-electricity-prices.js +248 -0
- package/dist/implementations/pricing/price-component-validators.d.ts +57 -0
- package/dist/implementations/pricing/price-component-validators.js +230 -0
- package/dist/implementations/pricing/price-schedule-resolver.d.ts +95 -0
- package/dist/implementations/pricing/price-schedule-resolver.js +175 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/packages/energy-app-charge.d.ts +8 -81
- package/dist/packages/energy-app-electricity-tariff.d.ts +153 -41
- package/dist/packages/energy-app-grid-fee.d.ts +199 -0
- package/dist/packages/energy-app-grid-fee.js +1 -0
- package/dist/packages/energy-app-modbus.d.ts +12 -0
- package/dist/types/enyo-charge.d.ts +0 -71
- package/dist/types/enyo-data-bus-value.d.ts +7 -0
- package/dist/types/enyo-electricity-tariff.d.ts +260 -50
- package/dist/types/enyo-electricity-tariff.js +71 -11
- package/dist/types/enyo-energy-prices.d.ts +9 -3
- package/dist/types/enyo-grid-fee.d.ts +247 -0
- package/dist/types/enyo-grid-fee.js +23 -0
- package/dist/types/enyo-price-composition.d.ts +147 -0
- package/dist/types/enyo-price-composition.js +12 -0
- package/dist/types/enyo-price-schedule.d.ts +137 -0
- package/dist/types/enyo-price-schedule.js +42 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -64,6 +64,14 @@ The official TypeScript SDK for building Energy Apps on the enyo platform. Creat
|
|
|
64
64
|
- [BatteryCommandForecast](#batterycommandforecast)
|
|
65
65
|
- [HeatpumpForecast](#heatpumpforecast)
|
|
66
66
|
- [Validators](#validators)
|
|
67
|
+
- [Dynamic Grid Fees & Tariff Bonuses](#dynamic-grid-fees--tariff-bonuses)
|
|
68
|
+
- [The rule that shapes this API](#the-rule-that-shapes-this-api)
|
|
69
|
+
- [Publishing a dynamic grid fee](#publishing-a-dynamic-grid-fee)
|
|
70
|
+
- [Declaring it on a tariff](#declaring-it-on-a-tariff)
|
|
71
|
+
- [Multiple bonuses, and how they stack](#multiple-bonuses-and-how-they-stack)
|
|
72
|
+
- [Reading the result](#reading-the-result)
|
|
73
|
+
- [Staying current](#staying-current)
|
|
74
|
+
- [Helpers](#helpers)
|
|
67
75
|
- [Automations](#automations)
|
|
68
76
|
- [The model](#-the-model)
|
|
69
77
|
- [Guide: Energy Manager apps](#-guide-energy-manager-apps)
|
|
@@ -170,6 +178,7 @@ The SDK exposes several layered building blocks. Pick the one that matches the k
|
|
|
170
178
|
| Register a weather / PV / dynamic-price forecast provider | [`useWeatherForecasting()`](#useweatherforecasting-energyappweatherforecasting) / [`usePvForecasting()`](#usepvforecasting-energyapppvforecasting) / [`useDynamicPriceForecast()`](#usedynamicpriceforecast-energyappdynamicpriceforecast) |
|
|
171
179
|
| Read EPEX SPOT wholesale prices (incl. negative-price windows) | [`useEpexSpotPrices()`](#useepexspotprices-energyappepexspotprice) |
|
|
172
180
|
| Manage electricity tariffs (default tariff, price per kWh) | [`useElectricityTariff()`](#useelectricitytariff-energyappelectricitytariff) |
|
|
181
|
+
| Publish or resolve time-variable grid fees (§14a HT/NT) | [`useGridFee()`](#usegridfee-energyappgridfee) |
|
|
173
182
|
| Register a PV system (kWp, DC strings, orientation) | [`usePvSystem()`](#usepvsystem-energyapppvsystem) |
|
|
174
183
|
| Discover capabilities of the active energy manager | [`useEnergyManager()`](#useenergymanager-energyappenergymanager) |
|
|
175
184
|
| Serve your v2 onboarding guides when the host asks for them | [`useOnboardingV2()`](#useonboardingv2-energyapponboardingv2) |
|
|
@@ -357,6 +366,7 @@ Energy Apps use a granular permissions system to control access to system resour
|
|
|
357
366
|
- **`PvSystemRegister`** / **`PvSystemUse`**: Register / read PV system configuration
|
|
358
367
|
- **`Savings`**: Publish and read back day-scoped savings reports
|
|
359
368
|
- **`EpexSpotPrices`**: Read EPEX SPOT day-ahead wholesale market prices
|
|
369
|
+
- **`GridFeeRegister`** / **`GridFeeUse`**: Publish / resolve dynamic grid fees (time-variable network charges)
|
|
360
370
|
|
|
361
371
|
#### Site & Identity Permissions
|
|
362
372
|
|
|
@@ -1222,20 +1232,84 @@ Three things to get right:
|
|
|
1222
1232
|
|
|
1223
1233
|
#### `useElectricityTariff(): EnergyAppElectricityTariff`
|
|
1224
1234
|
|
|
1225
|
-
|
|
1235
|
+
A site has **two tariff slots** — one for consumption, one for feed-in — and every method addresses
|
|
1236
|
+
them by `EnyoTariffDirectionEnum`. There is no tariff list, no default flag and no tariff id:
|
|
1237
|
+
occupying a slot is what it means to be the tariff in force.
|
|
1238
|
+
|
|
1239
|
+
Consumers read:
|
|
1226
1240
|
|
|
1227
1241
|
```typescript
|
|
1228
1242
|
const tariffs = energyApp.useElectricityTariff();
|
|
1229
1243
|
|
|
1230
|
-
await tariffs.
|
|
1231
|
-
await tariffs.
|
|
1244
|
+
const tariff = await tariffs.getTariff(EnyoTariffDirectionEnum.Consumption);
|
|
1245
|
+
const prices = await tariffs.getPrices(EnyoTariffDirectionEnum.Consumption, { fromIso, untilIso });
|
|
1246
|
+
|
|
1247
|
+
// Never add a component the series already contains.
|
|
1248
|
+
const needsGridFee = !prices?.includes.includes(EnyoPriceComponentEnum.GridFee);
|
|
1249
|
+
```
|
|
1250
|
+
|
|
1251
|
+
The app that integrates a provider owns the other side. It answers when the user picks it, sets the
|
|
1252
|
+
tariff once it is actually usable, and pushes prices as they arrive:
|
|
1253
|
+
|
|
1254
|
+
```typescript
|
|
1255
|
+
tariffs.onTariffSelected(async direction => {
|
|
1256
|
+
if (!await isAuthenticated()) {
|
|
1257
|
+
return {
|
|
1258
|
+
status: EnyoTariffActivationStatusEnum.AuthenticationRequired,
|
|
1259
|
+
authenticationUrl: buildOAuthUrl(direction),
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
await tariffs.setTariff(direction, {
|
|
1263
|
+
name: 'Spot 2026',
|
|
1264
|
+
vendorName: 'enyo',
|
|
1265
|
+
currency: EnyoCurrencyEnum.EUR,
|
|
1266
|
+
pricing: { type: EnyoTariffPricingTypeEnum.Dynamic },
|
|
1267
|
+
externalTariffId: contract.id,
|
|
1268
|
+
});
|
|
1269
|
+
return { status: EnyoTariffActivationStatusEnum.Success };
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
await tariffs.publishPrices(EnyoTariffDirectionEnum.Consumption, {
|
|
1273
|
+
includes: [EnyoPriceComponentEnum.GridFee], // ← we folded the fee in ourselves
|
|
1274
|
+
entries,
|
|
1275
|
+
});
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
**Calling `setTariff` is the activation signal.** Return `AuthenticationRequired` or
|
|
1279
|
+
`OnboardingRequired` from the handler to have the host send the user somewhere, and carry the
|
|
1280
|
+
`authenticationUrl` / `onboardingGuideId` that makes it actionable; when that flow later completes,
|
|
1281
|
+
call `setTariff` and the slot goes live. There is nothing else to report.
|
|
1282
|
+
|
|
1283
|
+
Prices are **pushed, not pulled** — providers publish day-ahead prices on their own schedule and
|
|
1284
|
+
rate-limit their APIs, so an app fetches when it makes sense and publishes what it got.
|
|
1232
1285
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1286
|
+
Listen for slot changes to know when to recompose:
|
|
1287
|
+
|
|
1288
|
+
```typescript
|
|
1289
|
+
const unsubscribe = tariffs.onTariffChanged(async event => {
|
|
1290
|
+
if (event.direction !== EnyoTariffDirectionEnum.Consumption) return;
|
|
1291
|
+
await recomposePrices(event.tariff);
|
|
1292
|
+
});
|
|
1293
|
+
// later
|
|
1294
|
+
unsubscribe();
|
|
1235
1295
|
```
|
|
1236
1296
|
|
|
1297
|
+
A tariff carries **no grid fee** — see [Dynamic Grid Fees & Tariff Bonuses](#dynamic-grid-fees--tariff-bonuses).
|
|
1298
|
+
|
|
1237
1299
|
Requires the `ElectricityTariff` permission.
|
|
1238
1300
|
|
|
1301
|
+
#### `useGridFee(): EnergyAppGridFee`
|
|
1302
|
+
|
|
1303
|
+
Publish time-dependent grid fees (network charges) and resolve them to a 15-minute series. See
|
|
1304
|
+
[Dynamic Grid Fees & Tariff Bonuses](#dynamic-grid-fees--tariff-bonuses) for the full picture.
|
|
1305
|
+
|
|
1306
|
+
```typescript
|
|
1307
|
+
const gridFee = energyApp.useGridFee();
|
|
1308
|
+
const fees = await gridFee.getGridFeeValues({ fromIso, untilIso });
|
|
1309
|
+
```
|
|
1310
|
+
|
|
1311
|
+
Publishers need `GridFeeRegister`; consumers need `GridFeeUse`.
|
|
1312
|
+
|
|
1239
1313
|
#### `useWeatherForecasting(): EnergyAppWeatherForecasting`
|
|
1240
1314
|
|
|
1241
1315
|
Register a weather-forecast provider (e.g. wraps an external API) and / or consume forecasts by zip code or coordinates.
|
|
@@ -2930,6 +3004,172 @@ try {
|
|
|
2930
3004
|
}
|
|
2931
3005
|
```
|
|
2932
3006
|
|
|
3007
|
+
## Dynamic Grid Fees & Tariff Bonuses
|
|
3008
|
+
|
|
3009
|
+
An electricity price is rarely one number. It is the energy price, plus the grid operator's network
|
|
3010
|
+
charge, minus whatever discounts the supplier grants. Both of the latter vary over time: §14a EnWG
|
|
3011
|
+
module 3 network charges switch between high- and low-tariff windows by time of day, weekday and
|
|
3012
|
+
season, and a supplier bonus may run "−5 ct/kWh between 22:00 and 06:00" or for a fixed promotional
|
|
3013
|
+
period.
|
|
3014
|
+
|
|
3015
|
+
### The rule that shapes this API
|
|
3016
|
+
|
|
3017
|
+
**The energy app composes the price. The host never adds anything.**
|
|
3018
|
+
|
|
3019
|
+
That is not a stylistic choice. Provider APIs disagree with each other: some already return
|
|
3020
|
+
grid-fee-inclusive prices, others return the bare energy price. Only the app that owns the tariff
|
|
3021
|
+
knows which — so it *declares* it on the price series it publishes, and composes accordingly. A host
|
|
3022
|
+
that silently added network charges would double-count for half the providers on the market.
|
|
3023
|
+
|
|
3024
|
+
```typescript
|
|
3025
|
+
const series = await energyApp.useElectricityTariff().getPrices(direction, { fromIso, untilIso });
|
|
3026
|
+
const fees = await energyApp.useGridFee().getGridFeeValues({ fromIso, untilIso });
|
|
3027
|
+
const spot = await energyApp.useEpexSpotPrices().getPrices({ fromIso, untilIso });
|
|
3028
|
+
|
|
3029
|
+
const prices = composeElectricityPrices({
|
|
3030
|
+
energyPrices: fromEpexSpotEntries(spot.entries),
|
|
3031
|
+
energyPriceComposition: series?.includes, // ← what the prices already contain
|
|
3032
|
+
gridFees: fromGridFeeEntries(fees), // ← gross cent → currency units
|
|
3033
|
+
bonuses: myBonuses,
|
|
3034
|
+
currency: 'EUR',
|
|
3035
|
+
});
|
|
3036
|
+
|
|
3037
|
+
const cheapest = prices.reduce((a, b) => (b.effectivePricePerKwh < a.effectivePricePerKwh ? b : a));
|
|
3038
|
+
```
|
|
3039
|
+
|
|
3040
|
+
### Publishing a dynamic grid fee
|
|
3041
|
+
|
|
3042
|
+
Amounts are always **unsigned magnitudes**: a grid fee is added, a bonus is subtracted. There is no
|
|
3043
|
+
signed number to get backwards.
|
|
3044
|
+
|
|
3045
|
+
```typescript
|
|
3046
|
+
await energyApp.useGridFee().registerGridFee({
|
|
3047
|
+
id: 'dso-hd-2026',
|
|
3048
|
+
name: 'Netzentgelt HT/NT 2026',
|
|
3049
|
+
gridOperator: 'Netze BW',
|
|
3050
|
+
currency: EnyoCurrencyEnum.EUR,
|
|
3051
|
+
timezone: 'Europe/Berlin', // required — windows are wall-clock, and DST is real
|
|
3052
|
+
appliesTo: EnyoPriceAppliesToEnum.Consumption,
|
|
3053
|
+
moduleThreeCompliant: true,
|
|
3054
|
+
schedule: {
|
|
3055
|
+
type: EnyoPriceScheduleTypeEnum.Recurring,
|
|
3056
|
+
windows: [
|
|
3057
|
+
{ startTimeOfDay: '06:00', endTimeOfDay: '22:00', amountPerKwh: 0.0912 },
|
|
3058
|
+
{ startTimeOfDay: '22:00', endTimeOfDay: '06:00', amountPerKwh: 0.0431 },
|
|
3059
|
+
],
|
|
3060
|
+
},
|
|
3061
|
+
});
|
|
3062
|
+
```
|
|
3063
|
+
|
|
3064
|
+
Three schedule types cover what grid operators and suppliers actually publish:
|
|
3065
|
+
|
|
3066
|
+
| Type | Use it for |
|
|
3067
|
+
|---|---|
|
|
3068
|
+
| `Constant` | one amount that never changes |
|
|
3069
|
+
| `Recurring` | HT/NT, weekday-dependent and seasonal windows (`daysOfWeek`, `months`) |
|
|
3070
|
+
| `Absolute` | dated series, e.g. a day-ahead publication — republish with `registerGridFee()` |
|
|
3071
|
+
|
|
3072
|
+
A `Recurring` window may wrap past midnight (`22:00` → `06:00`). When it does, `daysOfWeek` and
|
|
3073
|
+
`months` are evaluated against the day the window **starts** — a Monday-only 22:00–06:00 window covers
|
|
3074
|
+
Monday evening *and* the early hours of Tuesday.
|
|
3075
|
+
|
|
3076
|
+
### Declaring it on a price series
|
|
3077
|
+
|
|
3078
|
+
A tariff carries **no grid fee at all**. It is neither a field on the tariff nor something the tariff
|
|
3079
|
+
declares a mode for: a network charge belongs to the site's grid connection, is registered
|
|
3080
|
+
independently with `useGridFee()`, and stays the same whichever supplier the site buys from. That is
|
|
3081
|
+
why `getGridFee()` takes no arguments and `getGridFeeValues()` takes no tariff selector.
|
|
3082
|
+
|
|
3083
|
+
What the owning app *does* declare is what its published prices already contain:
|
|
3084
|
+
|
|
3085
|
+
```typescript
|
|
3086
|
+
await energyApp.useElectricityTariff().publishPrices(EnyoTariffDirectionEnum.Consumption, {
|
|
3087
|
+
includes: [], // the bare energy price — the consumer adds the grid fee
|
|
3088
|
+
entries,
|
|
3089
|
+
});
|
|
3090
|
+
```
|
|
3091
|
+
|
|
3092
|
+
If the provider ships all-in prices instead, publish `includes: [EnyoPriceComponentEnum.GridFee]` and
|
|
3093
|
+
consumers will report the fee without adding it a second time. Declaring an included component you did
|
|
3094
|
+
not actually apply is the one mistake that silently under-prices every interval.
|
|
3095
|
+
|
|
3096
|
+
Bonuses work the same way — either apply them yourself and declare
|
|
3097
|
+
`EnyoPriceComponentEnum.Bonuses`, or hand them to `composeElectricityPrices()` and have them applied
|
|
3098
|
+
and reported separately:
|
|
3099
|
+
|
|
3100
|
+
```typescript
|
|
3101
|
+
const bonuses = [{
|
|
3102
|
+
id: 'night',
|
|
3103
|
+
name: 'Nachtbonus',
|
|
3104
|
+
timezone: 'Europe/Berlin',
|
|
3105
|
+
appliesTo: EnyoPriceAppliesToEnum.Consumption,
|
|
3106
|
+
schedule: {
|
|
3107
|
+
type: EnyoPriceScheduleTypeEnum.Recurring,
|
|
3108
|
+
windows: [{ startTimeOfDay: '22:00', endTimeOfDay: '06:00', amountPerKwh: 0.05 }],
|
|
3109
|
+
},
|
|
3110
|
+
}];
|
|
3111
|
+
```
|
|
3112
|
+
|
|
3113
|
+
### Multiple bonuses, and how they stack
|
|
3114
|
+
|
|
3115
|
+
A tariff may carry several bonuses at once. Overlapping bonuses **add up**, unless one of them is
|
|
3116
|
+
`exclusive` — then the exclusive bonus with the highest `priority` applies alone.
|
|
3117
|
+
|
|
3118
|
+
```typescript
|
|
3119
|
+
bonuses: [
|
|
3120
|
+
{ id: 'night', schedule: nightWindow, /* … */ }, // −5 ct
|
|
3121
|
+
{ id: 'summer', schedule: summerWindow, /* … */ }, // −2 ct → −7 ct together
|
|
3122
|
+
{ id: 'launch', schedule: promo, exclusive: true, priority: 10, /* … */ }, // wins alone where it applies
|
|
3123
|
+
]
|
|
3124
|
+
```
|
|
3125
|
+
|
|
3126
|
+
### Reading the result
|
|
3127
|
+
|
|
3128
|
+
Every component is reported separately, so a UI can explain the number and an optimizer can reason
|
|
3129
|
+
about its parts:
|
|
3130
|
+
|
|
3131
|
+
```typescript
|
|
3132
|
+
{
|
|
3133
|
+
timestampIso: '2026-09-02T22:00:00.000Z',
|
|
3134
|
+
energyPricePerKwh: 0.18,
|
|
3135
|
+
gridFeePerKwh: 0.0431,
|
|
3136
|
+
gridFeeOrigin: 'added', // 'included' when the provider already had it, 'not-applicable' when none
|
|
3137
|
+
bonusPerKwh: 0.05,
|
|
3138
|
+
bonusOrigin: 'added',
|
|
3139
|
+
appliedBonusIds: ['night'],
|
|
3140
|
+
effectivePricePerKwh: 0.1731,
|
|
3141
|
+
currency: 'EUR',
|
|
3142
|
+
}
|
|
3143
|
+
```
|
|
3144
|
+
|
|
3145
|
+
A component flagged `included` is still reported — you can break down an all-in price — but it is never
|
|
3146
|
+
added a second time. Effective prices may go negative, because wholesale prices do; pass
|
|
3147
|
+
`minEffectivePricePerKwh` only if your use case genuinely needs a floor.
|
|
3148
|
+
|
|
3149
|
+
### Staying current
|
|
3150
|
+
|
|
3151
|
+
Both sides change under you. Listen to both:
|
|
3152
|
+
|
|
3153
|
+
```typescript
|
|
3154
|
+
energyApp.useGridFee().onGridFeeChanged(() => recompose()); // new day-ahead fee series
|
|
3155
|
+
energyApp.useElectricityTariff().onTariffChanged(() => recompose()); // new default, bonus, or fee link
|
|
3156
|
+
```
|
|
3157
|
+
|
|
3158
|
+
### Helpers
|
|
3159
|
+
|
|
3160
|
+
| Function | Purpose |
|
|
3161
|
+
|---|---|
|
|
3162
|
+
| `composeElectricityPrices()` | energy price + grid fee − bonuses → effective price, with full breakdown |
|
|
3163
|
+
| `fromEnergyPriceEntries()` / `fromEpexSpotEntries()` | adapt the SDK's price shapes into composer input |
|
|
3164
|
+
| `resolveTariffBonuses()` | resolve a tariff's bonuses to 15-min intervals, stacking rule applied |
|
|
3165
|
+
| `resolvePriceSchedule()` | expand any schedule to a 15-min series (DST-aware, dependency-free) |
|
|
3166
|
+
| `validateDynamicGridFee()` / `validateTariffBonuses()` | catch overlapping windows and negative amounts before registering |
|
|
3167
|
+
|
|
3168
|
+
### Not covered
|
|
3169
|
+
|
|
3170
|
+
Consumption-quota bonuses — "the first 100 kWh each month are free", "1000 free charging kWh per year" —
|
|
3171
|
+
are **not** expressible. They need consumption accounting that this model deliberately does not carry.
|
|
3172
|
+
|
|
2933
3173
|
## Examples
|
|
2934
3174
|
|
|
2935
3175
|
### Basic Energy App
|
|
@@ -18,6 +18,7 @@ var EnergyAppPackageCategory;
|
|
|
18
18
|
EnergyAppPackageCategory["TemperatureSensor"] = "temperature-sensor";
|
|
19
19
|
EnergyAppPackageCategory["SmartPlug"] = "smart-plug";
|
|
20
20
|
EnergyAppPackageCategory["HeatingRod"] = "heating-rod";
|
|
21
|
+
EnergyAppPackageCategory["GridOperator"] = "grid-operator";
|
|
21
22
|
EnergyAppPackageCategory["Other"] = "other";
|
|
22
23
|
})(EnergyAppPackageCategory || (exports.EnergyAppPackageCategory = EnergyAppPackageCategory = {}));
|
|
23
24
|
/**
|
|
@@ -60,4 +60,6 @@ var EnergyAppPermissionTypeEnum;
|
|
|
60
60
|
EnergyAppPermissionTypeEnum["Savings"] = "Savings";
|
|
61
61
|
EnergyAppPermissionTypeEnum["EpexSpotPrices"] = "EpexSpotPrices";
|
|
62
62
|
EnergyAppPermissionTypeEnum["FirmwareRegistry"] = "FirmwareRegistry";
|
|
63
|
+
EnergyAppPermissionTypeEnum["GridFeeRegister"] = "GridFeeRegister";
|
|
64
|
+
EnergyAppPermissionTypeEnum["GridFeeUse"] = "GridFeeUse";
|
|
63
65
|
})(EnergyAppPermissionTypeEnum || (exports.EnergyAppPermissionTypeEnum = EnergyAppPermissionTypeEnum = {}));
|
|
@@ -55,7 +55,9 @@ export declare enum EnergyAppPermissionTypeEnum {
|
|
|
55
55
|
Automation = "Automation",
|
|
56
56
|
Savings = "Savings",
|
|
57
57
|
EpexSpotPrices = "EpexSpotPrices",
|
|
58
|
-
FirmwareRegistry = "FirmwareRegistry"
|
|
58
|
+
FirmwareRegistry = "FirmwareRegistry",
|
|
59
|
+
GridFeeRegister = "GridFeeRegister",
|
|
60
|
+
GridFeeUse = "GridFeeUse"
|
|
59
61
|
}
|
|
60
62
|
/**
|
|
61
63
|
* String union of every permission name, derived from
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -443,6 +443,28 @@ class EnergyApp {
|
|
|
443
443
|
useEpexSpotPrices() {
|
|
444
444
|
return this.energyAppSdk.useEpexSpotPrices();
|
|
445
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* Gets the Grid Fee API for publishing grid fees (network charges), asking
|
|
448
|
+
* which one applies, and resolving it to a 15-minute series.
|
|
449
|
+
*
|
|
450
|
+
* This is the single source for grid fees. A network charge belongs to the
|
|
451
|
+
* site's grid connection rather than to a supplier contract, so a tariff
|
|
452
|
+
* carries no fee of its own and declares nothing about how one is
|
|
453
|
+
* determined — constant and time-dependent fees are both registered and read
|
|
454
|
+
* here.
|
|
455
|
+
*
|
|
456
|
+
* Nothing published here is folded into a price automatically — provider
|
|
457
|
+
* APIs differ in whether their prices already contain the network charge.
|
|
458
|
+
* The app that owns the tariff declares that one fact via the tariff's
|
|
459
|
+
* the price series' `includes` and composes the effective price itself
|
|
460
|
+
* with `composeElectricityPrices()`.
|
|
461
|
+
* @returns The Grid Fee API instance
|
|
462
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `GridFeeRegister` or
|
|
463
|
+
* `GridFeeUse` permission required by the called method is not granted.
|
|
464
|
+
*/
|
|
465
|
+
useGridFee() {
|
|
466
|
+
return this.energyAppSdk.useGridFee();
|
|
467
|
+
}
|
|
446
468
|
/**
|
|
447
469
|
* Gets the current SDK version.
|
|
448
470
|
* @returns The semantic version string of the SDK
|
|
@@ -45,6 +45,7 @@ import { EnergyAppAutomation } from "./packages/energy-app-automation.cjs";
|
|
|
45
45
|
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
|
+
import { EnergyAppGridFee } from "./packages/energy-app-grid-fee.cjs";
|
|
48
49
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
49
50
|
/**
|
|
50
51
|
* Concrete implementation of {@link EnyoEnergyAppSdk} that delegates every call
|
|
@@ -366,6 +367,26 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
366
367
|
* permission is not granted.
|
|
367
368
|
*/
|
|
368
369
|
useEpexSpotPrices(): EnergyAppEpexSpotPrice;
|
|
370
|
+
/**
|
|
371
|
+
* Gets the Grid Fee API for publishing grid fees (network charges), asking
|
|
372
|
+
* which one applies, and resolving it to a 15-minute series.
|
|
373
|
+
*
|
|
374
|
+
* This is the single source for grid fees. A network charge belongs to the
|
|
375
|
+
* site's grid connection rather than to a supplier contract, so a tariff
|
|
376
|
+
* carries no fee of its own and declares nothing about how one is
|
|
377
|
+
* determined — constant and time-dependent fees are both registered and read
|
|
378
|
+
* here.
|
|
379
|
+
*
|
|
380
|
+
* Nothing published here is folded into a price automatically — provider
|
|
381
|
+
* APIs differ in whether their prices already contain the network charge.
|
|
382
|
+
* The app that owns the tariff declares that one fact via the tariff's
|
|
383
|
+
* the price series' `includes` and composes the effective price itself
|
|
384
|
+
* with `composeElectricityPrices()`.
|
|
385
|
+
* @returns The Grid Fee API instance
|
|
386
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `GridFeeRegister` or
|
|
387
|
+
* `GridFeeUse` permission required by the called method is not granted.
|
|
388
|
+
*/
|
|
389
|
+
useGridFee(): EnergyAppGridFee;
|
|
369
390
|
/**
|
|
370
391
|
* Gets the current SDK version.
|
|
371
392
|
* @returns The semantic version string of the SDK
|
|
@@ -44,6 +44,7 @@ import { EnergyAppAutomation } from "./packages/energy-app-automation.cjs";
|
|
|
44
44
|
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
|
+
import { EnergyAppGridFee } from "./packages/energy-app-grid-fee.cjs";
|
|
47
48
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
48
49
|
export declare enum EnergyAppStateEnum {
|
|
49
50
|
Launching = "launching",
|
|
@@ -163,4 +164,6 @@ export interface EnyoEnergyAppSdk {
|
|
|
163
164
|
useDeviceTest: () => EnergyAppDeviceTest;
|
|
164
165
|
/** Get the EPEX SPOT Price API for reading the cleared day-ahead wholesale electricity prices that apply to this device */
|
|
165
166
|
useEpexSpotPrices: () => EnergyAppEpexSpotPrice;
|
|
167
|
+
/** Get the Grid Fee API for publishing time-dependent network charges and resolving them to a 15-minute series */
|
|
168
|
+
useGridFee: () => EnergyAppGridFee;
|
|
166
169
|
}
|
|
@@ -290,12 +290,18 @@ class ApplianceManager {
|
|
|
290
290
|
const updatedListenerId = applianceService.listenForApplianceUpdated((appliance) => {
|
|
291
291
|
if (this.disposed)
|
|
292
292
|
return;
|
|
293
|
+
if (this.config.enableLogging) {
|
|
294
|
+
console.debug(`Appliance updated event for ${appliance.id}`);
|
|
295
|
+
}
|
|
293
296
|
this.updateCache(appliance);
|
|
294
297
|
});
|
|
295
298
|
this.listenerIds.push(updatedListenerId);
|
|
296
299
|
const removedListenerId = applianceService.listenForApplianceRemoved((applianceId) => {
|
|
297
300
|
if (this.disposed)
|
|
298
301
|
return;
|
|
302
|
+
if (this.config.enableLogging) {
|
|
303
|
+
console.debug(`Appliance removed event for ${applianceId}`);
|
|
304
|
+
}
|
|
299
305
|
this.removeFromCache(applianceId);
|
|
300
306
|
});
|
|
301
307
|
this.listenerIds.push(removedListenerId);
|