@enyo-energy/energy-app-sdk 1.2.0 → 1.3.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 (58) hide show
  1. package/README.md +211 -1
  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 +16 -0
  7. package/dist/cjs/energy-app.d.cts +15 -0
  8. package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
  9. package/dist/cjs/implementations/pricing/compose-electricity-prices.cjs +218 -0
  10. package/dist/cjs/implementations/pricing/compose-electricity-prices.d.cts +100 -0
  11. package/dist/cjs/implementations/pricing/price-component-validators.cjs +295 -0
  12. package/dist/cjs/implementations/pricing/price-component-validators.d.cts +90 -0
  13. package/dist/cjs/implementations/pricing/price-schedule-resolver.cjs +182 -0
  14. package/dist/cjs/implementations/pricing/price-schedule-resolver.d.cts +95 -0
  15. package/dist/cjs/index.cjs +7 -0
  16. package/dist/cjs/index.d.cts +7 -0
  17. package/dist/cjs/packages/energy-app-electricity-tariff.d.cts +34 -1
  18. package/dist/cjs/packages/energy-app-grid-fee.cjs +2 -0
  19. package/dist/cjs/packages/energy-app-grid-fee.d.cts +181 -0
  20. package/dist/cjs/types/enyo-electricity-tariff.cjs +33 -1
  21. package/dist/cjs/types/enyo-electricity-tariff.d.cts +161 -4
  22. package/dist/cjs/types/enyo-grid-fee.cjs +15 -0
  23. package/dist/cjs/types/enyo-grid-fee.d.cts +129 -0
  24. package/dist/cjs/types/enyo-price-composition.cjs +15 -0
  25. package/dist/cjs/types/enyo-price-composition.d.cts +126 -0
  26. package/dist/cjs/types/enyo-price-schedule.cjs +45 -0
  27. package/dist/cjs/types/enyo-price-schedule.d.cts +137 -0
  28. package/dist/cjs/version.cjs +1 -1
  29. package/dist/cjs/version.d.cts +1 -1
  30. package/dist/energy-app-package-definition.d.ts +1 -0
  31. package/dist/energy-app-package-definition.js +1 -0
  32. package/dist/energy-app-permission.type.d.ts +3 -1
  33. package/dist/energy-app-permission.type.js +2 -0
  34. package/dist/energy-app.d.ts +15 -0
  35. package/dist/energy-app.js +16 -0
  36. package/dist/enyo-energy-app-sdk.d.ts +3 -0
  37. package/dist/implementations/pricing/compose-electricity-prices.d.ts +100 -0
  38. package/dist/implementations/pricing/compose-electricity-prices.js +212 -0
  39. package/dist/implementations/pricing/price-component-validators.d.ts +90 -0
  40. package/dist/implementations/pricing/price-component-validators.js +285 -0
  41. package/dist/implementations/pricing/price-schedule-resolver.d.ts +95 -0
  42. package/dist/implementations/pricing/price-schedule-resolver.js +175 -0
  43. package/dist/index.d.ts +7 -0
  44. package/dist/index.js +7 -0
  45. package/dist/packages/energy-app-electricity-tariff.d.ts +34 -1
  46. package/dist/packages/energy-app-grid-fee.d.ts +181 -0
  47. package/dist/packages/energy-app-grid-fee.js +1 -0
  48. package/dist/types/enyo-electricity-tariff.d.ts +161 -4
  49. package/dist/types/enyo-electricity-tariff.js +32 -0
  50. package/dist/types/enyo-grid-fee.d.ts +129 -0
  51. package/dist/types/enyo-grid-fee.js +12 -0
  52. package/dist/types/enyo-price-composition.d.ts +126 -0
  53. package/dist/types/enyo-price-composition.js +12 -0
  54. package/dist/types/enyo-price-schedule.d.ts +137 -0
  55. package/dist/types/enyo-price-schedule.js +42 -0
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/package.json +1 -1
@@ -0,0 +1,95 @@
1
+ import { EnyoPriceSchedule, EnyoResolvedPriceEntry } from '../../types/enyo-price-schedule.js';
2
+ /**
3
+ * Length of one resolved interval, in minutes. Matches the resolution of
4
+ * `useElectricityPrices().getPrices()` so resolved schedules can be combined
5
+ * with a price series interval by interval.
6
+ */
7
+ export declare const PRICE_SCHEDULE_INTERVAL_MINUTES = 15;
8
+ /**
9
+ * Wall-clock calendar fields of an instant, as seen in a specific time zone.
10
+ */
11
+ export interface ZonedDateParts {
12
+ /** Full year, e.g. `2026`. */
13
+ year: number;
14
+ /** Month, `1` = January … `12` = December. */
15
+ month: number;
16
+ /** Day of the month, starting at `1`. */
17
+ day: number;
18
+ /** Day of the week, `0` = Sunday … `6` = Saturday. */
19
+ weekday: number;
20
+ /** Hour in 24-hour notation, `0`–`23`. */
21
+ hour: number;
22
+ /** Minute, `0`–`59`. */
23
+ minute: number;
24
+ /** Minutes since local midnight (`hour * 60 + minute`). */
25
+ minutesOfDay: number;
26
+ }
27
+ /**
28
+ * Projects an instant onto the wall clock of a time zone.
29
+ *
30
+ * Uses `Intl.DateTimeFormat`, so daylight-saving transitions are handled by the
31
+ * platform's time zone database without pulling in a dependency.
32
+ *
33
+ * @param date - The instant to project
34
+ * @param timezone - IANA time zone identifier, e.g. `'Europe/Berlin'`
35
+ * @returns The wall-clock calendar fields of `date` in `timezone`
36
+ * @throws {RangeError} If `timezone` is not a valid IANA identifier
37
+ */
38
+ export declare function getZonedDateParts(date: Date, timezone: string): ZonedDateParts;
39
+ /**
40
+ * Parses an `HH:mm` wall-clock time into minutes since midnight.
41
+ *
42
+ * @param value - Time in 24-hour `HH:mm` notation, e.g. `'22:00'`
43
+ * @returns Minutes since midnight, or `null` when the value is malformed
44
+ */
45
+ export declare function parseTimeOfDay(value: string): number | null;
46
+ /**
47
+ * Rounds an instant down to the start of its 15-minute interval.
48
+ *
49
+ * @param timestampMs - Epoch milliseconds
50
+ * @returns Epoch milliseconds of the interval start
51
+ */
52
+ export declare function floorToInterval(timestampMs: number): number;
53
+ /**
54
+ * Options for {@link resolvePriceSchedule}.
55
+ */
56
+ export interface ResolvePriceScheduleOptions {
57
+ /** Start of the range to resolve, in ISO format (inclusive). */
58
+ fromIso: string;
59
+ /** End of the range to resolve, in ISO format (exclusive). */
60
+ untilIso: string;
61
+ /** IANA time zone the schedule's wall-clock times are expressed in. */
62
+ timezone: string;
63
+ /** Optional start of the component's validity in ISO format (inclusive). */
64
+ validFromIso?: string;
65
+ /** Optional end of the component's validity in ISO format (exclusive). */
66
+ validUntilIso?: string;
67
+ }
68
+ /**
69
+ * Expands a price schedule into a flat 15-minute series over a time range.
70
+ *
71
+ * The range start is rounded down to the enclosing 15-minute interval. Intervals
72
+ * no window covers resolve to `amountPerKwh: 0`; intervals outside the
73
+ * component's validity window are omitted entirely, so an empty result means
74
+ * "this component does not apply here" rather than "it is free here".
75
+ *
76
+ * Windows are evaluated in array order and the first match wins — schedules are
77
+ * expected to be overlap-free, which the validators enforce at registration
78
+ * time.
79
+ *
80
+ * @param schedule - The schedule to expand
81
+ * @param options - Range, time zone and optional validity bounds
82
+ * @returns Resolved entries sorted ascending by `timestampIso`
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const entries = resolvePriceSchedule(
87
+ * {
88
+ * type: EnyoPriceScheduleTypeEnum.Recurring,
89
+ * windows: [{ startTimeOfDay: '22:00', endTimeOfDay: '06:00', amountPerKwh: 0.05 }],
90
+ * },
91
+ * { fromIso: '2026-09-02T00:00:00Z', untilIso: '2026-09-03T00:00:00Z', timezone: 'Europe/Berlin' },
92
+ * );
93
+ * ```
94
+ */
95
+ export declare function resolvePriceSchedule(schedule: EnyoPriceSchedule, options: ResolvePriceScheduleOptions): EnyoResolvedPriceEntry[];
@@ -0,0 +1,175 @@
1
+ import { EnyoPriceScheduleTypeEnum, } from '../../types/enyo-price-schedule.js';
2
+ /**
3
+ * Length of one resolved interval, in minutes. Matches the resolution of
4
+ * `useElectricityPrices().getPrices()` so resolved schedules can be combined
5
+ * with a price series interval by interval.
6
+ */
7
+ export const PRICE_SCHEDULE_INTERVAL_MINUTES = 15;
8
+ const INTERVAL_MS = PRICE_SCHEDULE_INTERVAL_MINUTES * 60 * 1000;
9
+ const DAY_MS = 24 * 60 * 60 * 1000;
10
+ const WEEKDAY_INDEX = {
11
+ Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6,
12
+ };
13
+ const formatterCache = new Map();
14
+ function zonedFormatter(timezone) {
15
+ let formatter = formatterCache.get(timezone);
16
+ if (formatter === undefined) {
17
+ formatter = new Intl.DateTimeFormat('en-US', {
18
+ timeZone: timezone,
19
+ hourCycle: 'h23',
20
+ year: 'numeric',
21
+ month: 'numeric',
22
+ day: 'numeric',
23
+ weekday: 'short',
24
+ hour: 'numeric',
25
+ minute: 'numeric',
26
+ });
27
+ formatterCache.set(timezone, formatter);
28
+ }
29
+ return formatter;
30
+ }
31
+ /**
32
+ * Projects an instant onto the wall clock of a time zone.
33
+ *
34
+ * Uses `Intl.DateTimeFormat`, so daylight-saving transitions are handled by the
35
+ * platform's time zone database without pulling in a dependency.
36
+ *
37
+ * @param date - The instant to project
38
+ * @param timezone - IANA time zone identifier, e.g. `'Europe/Berlin'`
39
+ * @returns The wall-clock calendar fields of `date` in `timezone`
40
+ * @throws {RangeError} If `timezone` is not a valid IANA identifier
41
+ */
42
+ export function getZonedDateParts(date, timezone) {
43
+ const parts = zonedFormatter(timezone).formatToParts(date);
44
+ const lookup = {};
45
+ for (const part of parts) {
46
+ lookup[part.type] = part.value;
47
+ }
48
+ const hour = Number(lookup.hour);
49
+ const minute = Number(lookup.minute);
50
+ return {
51
+ year: Number(lookup.year),
52
+ month: Number(lookup.month),
53
+ day: Number(lookup.day),
54
+ weekday: WEEKDAY_INDEX[lookup.weekday] ?? 0,
55
+ hour,
56
+ minute,
57
+ minutesOfDay: hour * 60 + minute,
58
+ };
59
+ }
60
+ /**
61
+ * Parses an `HH:mm` wall-clock time into minutes since midnight.
62
+ *
63
+ * @param value - Time in 24-hour `HH:mm` notation, e.g. `'22:00'`
64
+ * @returns Minutes since midnight, or `null` when the value is malformed
65
+ */
66
+ export function parseTimeOfDay(value) {
67
+ const match = /^([01]\d|2[0-3]):([0-5]\d)$/.exec(value);
68
+ if (match === null) {
69
+ return null;
70
+ }
71
+ return Number(match[1]) * 60 + Number(match[2]);
72
+ }
73
+ /**
74
+ * Rounds an instant down to the start of its 15-minute interval.
75
+ *
76
+ * @param timestampMs - Epoch milliseconds
77
+ * @returns Epoch milliseconds of the interval start
78
+ */
79
+ export function floorToInterval(timestampMs) {
80
+ return Math.floor(timestampMs / INTERVAL_MS) * INTERVAL_MS;
81
+ }
82
+ function matchesRecurringWindow(window, timestampMs, timezone) {
83
+ const start = parseTimeOfDay(window.startTimeOfDay);
84
+ const end = parseTimeOfDay(window.endTimeOfDay);
85
+ if (start === null || end === null || start === end) {
86
+ return false;
87
+ }
88
+ const parts = getZonedDateParts(new Date(timestampMs), timezone);
89
+ let inWindow;
90
+ let anchor;
91
+ if (start < end) {
92
+ inWindow = parts.minutesOfDay >= start && parts.minutesOfDay < end;
93
+ anchor = parts;
94
+ }
95
+ else if (parts.minutesOfDay >= start) {
96
+ // Evening leg of a window that wraps past midnight.
97
+ inWindow = true;
98
+ anchor = parts;
99
+ }
100
+ else if (parts.minutesOfDay < end) {
101
+ // Early-morning leg — the window belongs to the previous local day.
102
+ inWindow = true;
103
+ anchor = getZonedDateParts(new Date(timestampMs - DAY_MS), timezone);
104
+ }
105
+ else {
106
+ return false;
107
+ }
108
+ if (!inWindow) {
109
+ return false;
110
+ }
111
+ if (window.daysOfWeek !== undefined && !window.daysOfWeek.includes(anchor.weekday)) {
112
+ return false;
113
+ }
114
+ return window.months === undefined || window.months.includes(anchor.month);
115
+ }
116
+ function matchesAbsoluteWindow(window, timestampMs) {
117
+ return timestampMs >= Date.parse(window.startIso) && timestampMs < Date.parse(window.endIso);
118
+ }
119
+ function amountAt(schedule, timestampMs, timezone) {
120
+ if (schedule.type === EnyoPriceScheduleTypeEnum.Constant) {
121
+ return schedule.amountPerKwh;
122
+ }
123
+ if (schedule.type === EnyoPriceScheduleTypeEnum.Absolute) {
124
+ const window = schedule.windows.find(candidate => matchesAbsoluteWindow(candidate, timestampMs));
125
+ return window?.amountPerKwh ?? 0;
126
+ }
127
+ const window = schedule.windows.find(candidate => matchesRecurringWindow(candidate, timestampMs, timezone));
128
+ return window?.amountPerKwh ?? 0;
129
+ }
130
+ /**
131
+ * Expands a price schedule into a flat 15-minute series over a time range.
132
+ *
133
+ * The range start is rounded down to the enclosing 15-minute interval. Intervals
134
+ * no window covers resolve to `amountPerKwh: 0`; intervals outside the
135
+ * component's validity window are omitted entirely, so an empty result means
136
+ * "this component does not apply here" rather than "it is free here".
137
+ *
138
+ * Windows are evaluated in array order and the first match wins — schedules are
139
+ * expected to be overlap-free, which the validators enforce at registration
140
+ * time.
141
+ *
142
+ * @param schedule - The schedule to expand
143
+ * @param options - Range, time zone and optional validity bounds
144
+ * @returns Resolved entries sorted ascending by `timestampIso`
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * const entries = resolvePriceSchedule(
149
+ * {
150
+ * type: EnyoPriceScheduleTypeEnum.Recurring,
151
+ * windows: [{ startTimeOfDay: '22:00', endTimeOfDay: '06:00', amountPerKwh: 0.05 }],
152
+ * },
153
+ * { fromIso: '2026-09-02T00:00:00Z', untilIso: '2026-09-03T00:00:00Z', timezone: 'Europe/Berlin' },
154
+ * );
155
+ * ```
156
+ */
157
+ export function resolvePriceSchedule(schedule, options) {
158
+ const untilMs = Date.parse(options.untilIso);
159
+ const validFromMs = options.validFromIso === undefined ? undefined : Date.parse(options.validFromIso);
160
+ const validUntilMs = options.validUntilIso === undefined ? undefined : Date.parse(options.validUntilIso);
161
+ const entries = [];
162
+ for (let cursor = floorToInterval(Date.parse(options.fromIso)); cursor < untilMs; cursor += INTERVAL_MS) {
163
+ if (validFromMs !== undefined && cursor < validFromMs) {
164
+ continue;
165
+ }
166
+ if (validUntilMs !== undefined && cursor >= validUntilMs) {
167
+ break;
168
+ }
169
+ entries.push({
170
+ timestampIso: new Date(cursor).toISOString(),
171
+ amountPerKwh: amountAt(schedule, cursor, options.timezone),
172
+ });
173
+ }
174
+ return entries;
175
+ }
package/dist/index.d.ts CHANGED
@@ -136,3 +136,10 @@ export * from './packages/energy-app-epex-spot-price.js';
136
136
  export * from './types/enyo-automation.js';
137
137
  export * from './packages/energy-app-automation.js';
138
138
  export * from './implementations/automation/automation-validators.js';
139
+ export * from './types/enyo-price-schedule.js';
140
+ export * from './types/enyo-grid-fee.js';
141
+ export * from './types/enyo-price-composition.js';
142
+ export * from './packages/energy-app-grid-fee.js';
143
+ export * from './implementations/pricing/price-schedule-resolver.js';
144
+ export * from './implementations/pricing/price-component-validators.js';
145
+ export * from './implementations/pricing/compose-electricity-prices.js';
package/dist/index.js CHANGED
@@ -136,3 +136,10 @@ export * from './packages/energy-app-epex-spot-price.js';
136
136
  export * from './types/enyo-automation.js';
137
137
  export * from './packages/energy-app-automation.js';
138
138
  export * from './implementations/automation/automation-validators.js';
139
+ export * from './types/enyo-price-schedule.js';
140
+ export * from './types/enyo-grid-fee.js';
141
+ export * from './types/enyo-price-composition.js';
142
+ export * from './packages/energy-app-grid-fee.js';
143
+ export * from './implementations/pricing/price-schedule-resolver.js';
144
+ export * from './implementations/pricing/price-component-validators.js';
145
+ export * from './implementations/pricing/compose-electricity-prices.js';
@@ -1,4 +1,4 @@
1
- import { EnyoElectricityTariff, EnyoElectricityTariffWithDefault } from "../types/enyo-electricity-tariff.js";
1
+ import { EnyoElectricityTariff, EnyoElectricityTariffWithDefault, EnyoTariffChangeEvent } from "../types/enyo-electricity-tariff.js";
2
2
  /**
3
3
  * Interface for managing electricity tariffs.
4
4
  * Provides methods to register, retrieve, and remove electricity tariffs
@@ -60,4 +60,37 @@ export interface EnergyAppElectricityTariff {
60
60
  * @returns Promise that resolves to the full updated tariff with its default indicator
61
61
  */
62
62
  updateTariff(id: string, attributes: Partial<Omit<EnyoElectricityTariff, 'id'>>): Promise<EnyoElectricityTariffWithDefault>;
63
+ /**
64
+ * Registers a listener invoked whenever any tariff is registered, updated
65
+ * or removed, or whenever a different tariff becomes the system default.
66
+ *
67
+ * Listeners see changes made by every energy app on the system, not just
68
+ * this one — filter on {@link EnyoTariffChangeEvent.tariffId} when only one
69
+ * tariff matters. This is the signal to recompose prices: a changed grid fee
70
+ * link, an added or expired bonus, or a new default tariff all invalidate a
71
+ * previously composed price series.
72
+ *
73
+ * Requires the `ElectricityTariff` permission.
74
+ *
75
+ * @param listener - Callback invoked with the tariff change event
76
+ * @returns A unique listener id that can be passed to {@link offTariffChanged}
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const listenerId = tariffs.onTariffChanged(async event => {
81
+ * if (event.changeType === EnyoTariffChangeTypeEnum.Removed) return;
82
+ * await recomposePrices(event.tariff);
83
+ * });
84
+ * // later
85
+ * tariffs.offTariffChanged(listenerId);
86
+ * ```
87
+ */
88
+ onTariffChanged(listener: (event: EnyoTariffChangeEvent) => void | Promise<void>): string;
89
+ /**
90
+ * Removes a previously registered tariff change listener. If the listener id
91
+ * is unknown, this operation is a no-op.
92
+ *
93
+ * @param listenerId - The id returned from {@link onTariffChanged}
94
+ */
95
+ offTariffChanged(listenerId: string): void;
63
96
  }
@@ -0,0 +1,181 @@
1
+ import { EnyoDynamicGridFee, EnyoDynamicGridFeeFilter, EnyoDynamicGridFeeRegistration, EnyoDynamicGridFeeSeries, EnyoGridFeeChangeEvent } from '../types/enyo-grid-fee.js';
2
+ import { EnyoAbsolutePriceWindow } from '../types/enyo-price-schedule.js';
3
+ /**
4
+ * Interface for publishing and consuming dynamic grid fees (network charges).
5
+ *
6
+ * Any energy app can act as a producer by registering a grid fee — typically an
7
+ * app that knows the local grid operator's tariff sheet or fetches a published
8
+ * series — and any energy app can act as a consumer by resolving that fee for a
9
+ * time range with {@link getDynamicGridFees}.
10
+ *
11
+ * Publishing requires the `GridFeeRegister` permission; consuming requires
12
+ * `GridFeeUse`.
13
+ *
14
+ * A grid fee describes the site's **grid connection**, not a supplier contract:
15
+ * it is registered and resolved independently of any tariff, and the same fee
16
+ * applies whichever supplier the site buys from.
17
+ *
18
+ * **Nothing here is applied to a price automatically.** The host never folds a
19
+ * grid fee into {@link EnergyAppEnergyPrices.getPrices}, because provider APIs
20
+ * differ: some already return grid-fee-inclusive prices, some do not. The app
21
+ * that owns the tariff knows which is the case — it declares it via the
22
+ * tariff's `priceComposition` and composes the effective price itself with
23
+ * `composeElectricityPrices()`.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // Producer: a §14a EnWG module 3 HT/NT network charge
28
+ * const gridFee = energyApp.useGridFee();
29
+ * await gridFee.registerGridFee({
30
+ * id: 'dso-hd-2026',
31
+ * name: 'Netzentgelt HT/NT 2026',
32
+ * gridOperator: 'Netze BW',
33
+ * currency: EnyoCurrencyEnum.EUR,
34
+ * timezone: 'Europe/Berlin',
35
+ * appliesTo: EnyoPriceAppliesToEnum.Consumption,
36
+ * moduleThreeCompliant: true,
37
+ * schedule: {
38
+ * type: EnyoPriceScheduleTypeEnum.Recurring,
39
+ * windows: [
40
+ * { startTimeOfDay: '06:00', endTimeOfDay: '22:00', amountPerKwh: 0.0912 },
41
+ * { startTimeOfDay: '22:00', endTimeOfDay: '06:00', amountPerKwh: 0.0431 },
42
+ * ],
43
+ * },
44
+ * });
45
+ *
46
+ * // Consumer: resolve it for the next 24 hours
47
+ * const fees = await gridFee.getDynamicGridFees({
48
+ * fromIso: new Date().toISOString(),
49
+ * untilIso: new Date(Date.now() + 86_400_000).toISOString(),
50
+ * });
51
+ * ```
52
+ */
53
+ export interface EnergyAppGridFee {
54
+ /**
55
+ * Registers a new dynamic grid fee or updates an existing one. Uses upsert
56
+ * logic based on `id` — if a fee with the same id already exists it is
57
+ * replaced, otherwise a new one is created.
58
+ *
59
+ * Requires the `GridFeeRegister` permission.
60
+ *
61
+ * @param registration - The grid fee to register, including its schedule
62
+ * @returns Promise that resolves to the stored grid fee, including the host-assigned `publishedAtIso`
63
+ */
64
+ registerGridFee(registration: EnyoDynamicGridFeeRegistration): Promise<EnyoDynamicGridFee>;
65
+ /**
66
+ * Partially updates a registered grid fee. Only the provided attributes are
67
+ * modified; all other fields remain unchanged.
68
+ *
69
+ * Requires the `GridFeeRegister` permission.
70
+ *
71
+ * @param id - The unique identifier of the grid fee to update
72
+ * @param attributes - A partial set of grid fee fields to update (excluding `id`)
73
+ * @returns Promise that resolves to the full updated grid fee
74
+ */
75
+ updateGridFee(id: string, attributes: Partial<Omit<EnyoDynamicGridFeeRegistration, 'id'>>): Promise<EnyoDynamicGridFee>;
76
+ /**
77
+ * Removes a registered grid fee. {@link getDynamicGridFees} resolves to
78
+ * `null` for it afterwards. If the fee does not exist, this operation is a
79
+ * no-op.
80
+ *
81
+ * Requires the `GridFeeRegister` permission.
82
+ *
83
+ * @param id - The unique identifier of the grid fee to remove
84
+ * @returns Promise that resolves when the grid fee has been removed
85
+ */
86
+ removeGridFee(id: string): Promise<void>;
87
+ /**
88
+ * Retrieves all registered grid fees across all energy apps on the system.
89
+ *
90
+ * Requires the `GridFeeUse` permission.
91
+ *
92
+ * @returns Promise that resolves to an array of all registered grid fees
93
+ */
94
+ listGridFees(): Promise<EnyoDynamicGridFee[]>;
95
+ /**
96
+ * Retrieves a single registered grid fee by its id, or `null` when no fee
97
+ * with that id exists.
98
+ *
99
+ * Requires the `GridFeeUse` permission.
100
+ *
101
+ * @param id - The unique identifier of the grid fee
102
+ * @returns Promise that resolves to the grid fee, or `null`
103
+ */
104
+ getGridFee(id: string): Promise<EnyoDynamicGridFee | null>;
105
+ /**
106
+ * Replaces the absolute windows of a grid fee whose schedule is
107
+ * {@link EnyoPriceScheduleTypeEnum.Absolute}. Use this for fees the grid
108
+ * operator publishes as a dated series (for example day-ahead), where the
109
+ * schedule changes far more often than the fee's metadata.
110
+ *
111
+ * Windows must not overlap and are stored sorted ascending by `startIso`.
112
+ * Publishing replaces the previously stored windows in full.
113
+ *
114
+ * Requires the `GridFeeRegister` permission.
115
+ *
116
+ * @param id - The unique identifier of the registered grid fee
117
+ * @param windows - The complete set of absolute windows to store
118
+ * @returns Promise that resolves when the windows have been stored and change listeners dispatched
119
+ */
120
+ publishGridFeeWindows(id: string, windows: EnyoAbsolutePriceWindow[]): Promise<void>;
121
+ /**
122
+ * Resolves a dynamic grid fee to a flat **15-minute series** over the
123
+ * requested range — the same resolution
124
+ * {@link EnergyAppEnergyPrices.getPrices} uses, so both series can be
125
+ * combined interval by interval.
126
+ *
127
+ * The fee is selected by `gridFeeId` or `meteringPointId`; with no selector
128
+ * the grid fee that applies to this device is used. Grid fees are **not**
129
+ * bound to a tariff — a network charge is a property of the grid connection,
130
+ * and the same fee applies no matter which supplier the site buys from.
131
+ * Returns `null` when no fee matches the selector.
132
+ *
133
+ * Intervals no window covers resolve to `feePerKwh: 0`, and intervals
134
+ * outside the fee's `validFromIso` / `validUntilIso` window are omitted.
135
+ *
136
+ * Requires the `GridFeeUse` permission.
137
+ *
138
+ * @param filter - The time range and the selector identifying which fee to resolve
139
+ * @returns Promise that resolves to the 15-minute fee series, or `null` when no fee applies
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const fees = await gridFee.getDynamicGridFees({
144
+ * fromIso: '2026-09-02T00:00:00Z',
145
+ * untilIso: '2026-09-03T00:00:00Z',
146
+ * });
147
+ * fees?.entries.forEach(e => console.log(e.timestampIso, e.feePerKwh));
148
+ * ```
149
+ */
150
+ getDynamicGridFees(filter: EnyoDynamicGridFeeFilter): Promise<EnyoDynamicGridFeeSeries | null>;
151
+ /**
152
+ * Registers a listener invoked whenever any grid fee is registered, updated
153
+ * or removed — including when its absolute windows are republished.
154
+ *
155
+ * Use it to recompute composed prices without polling: a grid operator that
156
+ * publishes a new day-ahead series mid-day changes the cost of every future
157
+ * interval.
158
+ *
159
+ * Requires the `GridFeeUse` permission.
160
+ *
161
+ * @param listener - Callback invoked with the change event
162
+ * @returns A unique listener id that can be passed to {@link offGridFeeChanged}
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const id = gridFee.onGridFeeChanged(async event => {
167
+ * if (event.gridFeeId === myFeeId) await recomputePrices();
168
+ * });
169
+ * // later
170
+ * gridFee.offGridFeeChanged(id);
171
+ * ```
172
+ */
173
+ onGridFeeChanged(listener: (event: EnyoGridFeeChangeEvent) => void | Promise<void>): string;
174
+ /**
175
+ * Removes a previously registered grid fee change listener. If the listener
176
+ * id is unknown, this operation is a no-op.
177
+ *
178
+ * @param listenerId - The id returned from {@link onGridFeeChanged}
179
+ */
180
+ offGridFeeChanged(listenerId: string): void;
181
+ }
@@ -0,0 +1 @@
1
+ export {};