@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.
Files changed (70) hide show
  1. package/README.md +245 -5
  2. package/dist/cjs/energy-app-package-definition.cjs +1 -0
  3. package/dist/cjs/energy-app-package-definition.d.cts +1 -0
  4. package/dist/cjs/energy-app-permission.type.cjs +2 -0
  5. package/dist/cjs/energy-app-permission.type.d.cts +3 -1
  6. package/dist/cjs/energy-app.cjs +22 -0
  7. package/dist/cjs/energy-app.d.cts +21 -0
  8. package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
  9. package/dist/cjs/implementations/appliances/appliance-manager.cjs +6 -0
  10. package/dist/cjs/implementations/pricing/compose-electricity-prices.cjs +255 -0
  11. package/dist/cjs/implementations/pricing/compose-electricity-prices.d.cts +125 -0
  12. package/dist/cjs/implementations/pricing/price-component-validators.cjs +238 -0
  13. package/dist/cjs/implementations/pricing/price-component-validators.d.cts +57 -0
  14. package/dist/cjs/implementations/pricing/price-schedule-resolver.cjs +182 -0
  15. package/dist/cjs/implementations/pricing/price-schedule-resolver.d.cts +95 -0
  16. package/dist/cjs/index.cjs +7 -0
  17. package/dist/cjs/index.d.cts +7 -0
  18. package/dist/cjs/packages/energy-app-charge.d.cts +8 -81
  19. package/dist/cjs/packages/energy-app-electricity-tariff.d.cts +153 -41
  20. package/dist/cjs/packages/energy-app-grid-fee.cjs +2 -0
  21. package/dist/cjs/packages/energy-app-grid-fee.d.cts +199 -0
  22. package/dist/cjs/packages/energy-app-modbus.d.cts +12 -0
  23. package/dist/cjs/types/enyo-charge.d.cts +0 -71
  24. package/dist/cjs/types/enyo-data-bus-value.d.cts +7 -0
  25. package/dist/cjs/types/enyo-electricity-tariff.cjs +73 -13
  26. package/dist/cjs/types/enyo-electricity-tariff.d.cts +260 -50
  27. package/dist/cjs/types/enyo-energy-prices.d.cts +9 -3
  28. package/dist/cjs/types/enyo-grid-fee.cjs +26 -0
  29. package/dist/cjs/types/enyo-grid-fee.d.cts +247 -0
  30. package/dist/cjs/types/enyo-price-composition.cjs +15 -0
  31. package/dist/cjs/types/enyo-price-composition.d.cts +147 -0
  32. package/dist/cjs/types/enyo-price-schedule.cjs +45 -0
  33. package/dist/cjs/types/enyo-price-schedule.d.cts +137 -0
  34. package/dist/cjs/version.cjs +1 -1
  35. package/dist/cjs/version.d.cts +1 -1
  36. package/dist/energy-app-package-definition.d.ts +1 -0
  37. package/dist/energy-app-package-definition.js +1 -0
  38. package/dist/energy-app-permission.type.d.ts +3 -1
  39. package/dist/energy-app-permission.type.js +2 -0
  40. package/dist/energy-app.d.ts +21 -0
  41. package/dist/energy-app.js +22 -0
  42. package/dist/enyo-energy-app-sdk.d.ts +3 -0
  43. package/dist/implementations/appliances/appliance-manager.js +6 -0
  44. package/dist/implementations/pricing/compose-electricity-prices.d.ts +125 -0
  45. package/dist/implementations/pricing/compose-electricity-prices.js +248 -0
  46. package/dist/implementations/pricing/price-component-validators.d.ts +57 -0
  47. package/dist/implementations/pricing/price-component-validators.js +230 -0
  48. package/dist/implementations/pricing/price-schedule-resolver.d.ts +95 -0
  49. package/dist/implementations/pricing/price-schedule-resolver.js +175 -0
  50. package/dist/index.d.ts +7 -0
  51. package/dist/index.js +7 -0
  52. package/dist/packages/energy-app-charge.d.ts +8 -81
  53. package/dist/packages/energy-app-electricity-tariff.d.ts +153 -41
  54. package/dist/packages/energy-app-grid-fee.d.ts +199 -0
  55. package/dist/packages/energy-app-grid-fee.js +1 -0
  56. package/dist/packages/energy-app-modbus.d.ts +12 -0
  57. package/dist/types/enyo-charge.d.ts +0 -71
  58. package/dist/types/enyo-data-bus-value.d.ts +7 -0
  59. package/dist/types/enyo-electricity-tariff.d.ts +260 -50
  60. package/dist/types/enyo-electricity-tariff.js +71 -11
  61. package/dist/types/enyo-energy-prices.d.ts +9 -3
  62. package/dist/types/enyo-grid-fee.d.ts +247 -0
  63. package/dist/types/enyo-grid-fee.js +23 -0
  64. package/dist/types/enyo-price-composition.d.ts +147 -0
  65. package/dist/types/enyo-price-composition.js +12 -0
  66. package/dist/types/enyo-price-schedule.d.ts +137 -0
  67. package/dist/types/enyo-price-schedule.js +42 -0
  68. package/dist/version.d.ts +1 -1
  69. package/dist/version.js +1 -1
  70. 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
- Register, retrieve, and manage electricity tariffs. One tariff can be marked as the system default.
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.registerTariff({ id: 't1', name: 'Spot 2026', pricePerKwh: 0.21 });
1231
- await tariffs.makeDefaultTariff('t1');
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
- const defaultTariff = await tariffs.getDefaultTariff();
1234
- const all = await tariffs.getAllTariffs();
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
  /**
@@ -16,6 +16,7 @@ export declare enum EnergyAppPackageCategory {
16
16
  TemperatureSensor = "temperature-sensor",
17
17
  SmartPlug = "smart-plug",
18
18
  HeatingRod = "heating-rod",
19
+ GridOperator = "grid-operator",
19
20
  Other = "other"
20
21
  }
21
22
  /**
@@ -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
@@ -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);