@enyo-energy/energy-app-sdk 0.0.185 → 0.0.186
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 +38 -0
- package/dist/cjs/energy-app-permission.type.cjs +1 -0
- package/dist/cjs/energy-app-permission.type.d.cts +2 -1
- package/dist/cjs/energy-app.cjs +16 -0
- package/dist/cjs/energy-app.d.cts +15 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/packages/energy-app-epex-spot-price.cjs +2 -0
- package/dist/cjs/packages/energy-app-epex-spot-price.d.cts +123 -0
- package/dist/cjs/types/enyo-data-bus-value.cjs +18 -1
- package/dist/cjs/types/enyo-data-bus-value.d.cts +44 -0
- package/dist/cjs/types/enyo-epex-spot-price.cjs +2 -0
- package/dist/cjs/types/enyo-epex-spot-price.d.cts +77 -0
- package/dist/cjs/types/enyo-inverter-appliance.d.cts +15 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app-permission.type.d.ts +2 -1
- package/dist/energy-app-permission.type.js +1 -0
- package/dist/energy-app.d.ts +15 -0
- package/dist/energy-app.js +16 -0
- package/dist/enyo-energy-app-sdk.d.ts +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/packages/energy-app-epex-spot-price.d.ts +123 -0
- package/dist/packages/energy-app-epex-spot-price.js +1 -0
- package/dist/types/enyo-data-bus-value.d.ts +44 -0
- package/dist/types/enyo-data-bus-value.js +17 -0
- package/dist/types/enyo-epex-spot-price.d.ts +77 -0
- package/dist/types/enyo-epex-spot-price.js +1 -0
- package/dist/types/enyo-inverter-appliance.d.ts +15 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -167,6 +167,7 @@ The SDK exposes several layered building blocks. Pick the one that matches the k
|
|
|
167
167
|
| Retrieve secrets from the developer org secret store | [`useSecretManager()`](#usesecretmanager-energyappsecretmanager) |
|
|
168
168
|
| Submit energy-manager diagnostics | [`useDiagnostics()`](#usediagnostics-energyappdiagnostics) |
|
|
169
169
|
| Register a weather / PV / dynamic-price forecast provider | [`useWeatherForecasting()`](#useweatherforecasting-energyappweatherforecasting) / [`usePvForecasting()`](#usepvforecasting-energyapppvforecasting) / [`useDynamicPriceForecast()`](#usedynamicpriceforecast-energyappdynamicpriceforecast) |
|
|
170
|
+
| Read EPEX SPOT wholesale prices (incl. negative-price windows) | [`useEpexSpotPrices()`](#useepexspotprices-energyappepexspotprice) |
|
|
170
171
|
| Manage electricity tariffs (default tariff, price per kWh) | [`useElectricityTariff()`](#useelectricitytariff-energyappelectricitytariff) |
|
|
171
172
|
| Register a PV system (kWp, DC strings, orientation) | [`usePvSystem()`](#usepvsystem-energyapppvsystem) |
|
|
172
173
|
| Discover capabilities of the active energy manager | [`useEnergyManager()`](#useenergymanager-energyappenergymanager) |
|
|
@@ -353,6 +354,7 @@ Energy Apps use a granular permissions system to control access to system resour
|
|
|
353
354
|
- **`DynamicPriceForecastRegister`** / **`DynamicPriceForecastUse`**: Publish / consume dynamic-price forecasts
|
|
354
355
|
- **`PvSystemRegister`** / **`PvSystemUse`**: Register / read PV system configuration
|
|
355
356
|
- **`Savings`**: Publish and read back day-scoped savings reports
|
|
357
|
+
- **`EpexSpotPrices`**: Read EPEX SPOT day-ahead wholesale market prices
|
|
356
358
|
|
|
357
359
|
#### Site & Identity Permissions
|
|
358
360
|
|
|
@@ -1139,6 +1141,42 @@ dpf.onForecastPublished((forecast) => console.log('new forecast', forecast.forec
|
|
|
1139
1141
|
|
|
1140
1142
|
Publishers need `DynamicPriceForecastRegister`; consumers need `DynamicPriceForecastUse`.
|
|
1141
1143
|
|
|
1144
|
+
#### `useEpexSpotPrices(): EnergyAppEpexSpotPrice`
|
|
1145
|
+
|
|
1146
|
+
Read the EPEX SPOT day-ahead wholesale prices the host caches for this device, so an energy manager can decide when to charge, when to run a flexible load, and when to stop exporting PV.
|
|
1147
|
+
|
|
1148
|
+
These are **raw market prices** — no grid fees, levies, taxes or supplier margin — and they go **negative** when supply outruns demand. For what the customer is billed use [`useElectricityPrices()`](#useelectricityprices-energyappenergyprices); for forecasts published by other apps use [`useDynamicPriceForecast()`](#usedynamicpriceforecast-energyappdynamicpriceforecast).
|
|
1149
|
+
|
|
1150
|
+
```typescript
|
|
1151
|
+
const epex = energyApp.useEpexSpotPrices();
|
|
1152
|
+
|
|
1153
|
+
const now = await epex.getCurrentSpotPrice();
|
|
1154
|
+
if (now && now.pricePerKwh < 0) {
|
|
1155
|
+
// feeding in costs money right now
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
const tomorrow = await epex.getSpotPrices({
|
|
1159
|
+
fromIso: '2026-08-13T00:00:00Z',
|
|
1160
|
+
untilIso: '2026-08-14T00:00:00Z'
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
// Pre-grouped runs of sub-zero periods — the shape curtailment logic wants.
|
|
1164
|
+
const windows = await epex.getNegativePriceWindows();
|
|
1165
|
+
|
|
1166
|
+
// Tomorrow's auction clears around 14:00 CET; re-plan when it lands.
|
|
1167
|
+
epex.onSpotPricesUpdated(prices => scheduler.replan(prices.entries));
|
|
1168
|
+
```
|
|
1169
|
+
|
|
1170
|
+
Notes worth respecting:
|
|
1171
|
+
|
|
1172
|
+
- **Read `resolution`, don't assume it.** EPEX SPOT day-ahead moved to 15-minute periods in 2025, but older data is still hourly.
|
|
1173
|
+
- **The series may be shorter than you asked for.** Before the day-ahead auction clears (14:00 CET/CEST), only today exists — check the last entry's `endTimestampIso`.
|
|
1174
|
+
- **`retrievedAtIso` tells you how stale the cache is** after the device has been offline.
|
|
1175
|
+
- Prices come both as `pricePerMwh` (the exchange's own unit) and `pricePerKwh` (the SDK's convention).
|
|
1176
|
+
- Inverter appliances carry a `blockFeedInOnNegativePrices` flag in their metadata (`EnyoInverterApplianceMetadata`). It is configuration, not state: whoever controls the inverter is responsible for curtailing export to 0 W while the price is negative and lifting the curtailment afterwards.
|
|
1177
|
+
|
|
1178
|
+
Requires the `EpexSpotPrices` permission.
|
|
1179
|
+
|
|
1142
1180
|
#### `usePvSystem(): EnergyAppPvSystem`
|
|
1143
1181
|
|
|
1144
1182
|
Register a PV system's structural configuration (kWp, DC string orientations, associated appliances) so other apps can reason about expected production.
|
|
@@ -51,5 +51,6 @@ var EnergyAppPermissionTypeEnum;
|
|
|
51
51
|
EnergyAppPermissionTypeEnum["ProvidedFiles"] = "ProvidedFiles";
|
|
52
52
|
EnergyAppPermissionTypeEnum["Automation"] = "Automation";
|
|
53
53
|
EnergyAppPermissionTypeEnum["Savings"] = "Savings";
|
|
54
|
+
EnergyAppPermissionTypeEnum["EpexSpotPrices"] = "EpexSpotPrices";
|
|
54
55
|
EnergyAppPermissionTypeEnum["FirmwareRegistry"] = "FirmwareRegistry";
|
|
55
56
|
})(EnergyAppPermissionTypeEnum || (exports.EnergyAppPermissionTypeEnum = EnergyAppPermissionTypeEnum = {}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type EnergyAppPermissionType = 'RestrictedInternetAccess' | 'NetworkDeviceDiscovery' | 'NetworkDeviceSearch' | 'NetworkDeviceAccess' | 'AllNetworkDeviceAccess' | 'Modbus' | 'Storage' | 'Appliance' | 'AllAppliances' | 'SendDataBusValues' | 'SubscribeDataBus' | 'SendDataBusCommands' | 'OcppServer' | 'ChargingCard' | 'Vehicle' | 'Charge' | 'SecretManager' | 'LocationZipCode' | 'LocationCoordinates' | 'Timeseries' | 'EnergyManagerInfo' | 'ElectricityTariff' | 'WeatherForecastRegister' | 'WeatherForecastUse' | 'PvForecastRegister' | 'PvForecastUse' | 'DynamicPriceForecastRegister' | 'DynamicPriceForecastUse' | 'PvSystemRegister' | 'PvSystemUse' | 'InverterControlCommands' | 'BatteryControlCommands' | 'BatteryStorageState' | 'ChargerControlCommands' | 'ModbusRtu' | 'EnergyPrices' | 'EnergyManager' | 'EebusDeviceManagement' | 'EebusDataAccess' | 'EebusControl' | 'Mqtt' | 'Bluetooth' | 'Wifi' | 'ChildProcess' | 'Udp' | 'ProvidedFiles' | 'Automation' | 'Savings' | 'FirmwareRegistry';
|
|
1
|
+
export type EnergyAppPermissionType = 'RestrictedInternetAccess' | 'NetworkDeviceDiscovery' | 'NetworkDeviceSearch' | 'NetworkDeviceAccess' | 'AllNetworkDeviceAccess' | 'Modbus' | 'Storage' | 'Appliance' | 'AllAppliances' | 'SendDataBusValues' | 'SubscribeDataBus' | 'SendDataBusCommands' | 'OcppServer' | 'ChargingCard' | 'Vehicle' | 'Charge' | 'SecretManager' | 'LocationZipCode' | 'LocationCoordinates' | 'Timeseries' | 'EnergyManagerInfo' | 'ElectricityTariff' | 'WeatherForecastRegister' | 'WeatherForecastUse' | 'PvForecastRegister' | 'PvForecastUse' | 'DynamicPriceForecastRegister' | 'DynamicPriceForecastUse' | 'PvSystemRegister' | 'PvSystemUse' | 'InverterControlCommands' | 'BatteryControlCommands' | 'BatteryStorageState' | 'ChargerControlCommands' | 'ModbusRtu' | 'EnergyPrices' | 'EnergyManager' | 'EebusDeviceManagement' | 'EebusDataAccess' | 'EebusControl' | 'Mqtt' | 'Bluetooth' | 'Wifi' | 'ChildProcess' | 'Udp' | 'ProvidedFiles' | 'Automation' | 'Savings' | 'EpexSpotPrices' | 'FirmwareRegistry';
|
|
2
2
|
export declare enum EnergyAppPermissionTypeEnum {
|
|
3
3
|
RestrictedInternetAccess = "RestrictedInternetAccess",
|
|
4
4
|
NetworkDeviceDiscovery = "NetworkDeviceDiscovery",
|
|
@@ -48,5 +48,6 @@ export declare enum EnergyAppPermissionTypeEnum {
|
|
|
48
48
|
ProvidedFiles = "ProvidedFiles",
|
|
49
49
|
Automation = "Automation",
|
|
50
50
|
Savings = "Savings",
|
|
51
|
+
EpexSpotPrices = "EpexSpotPrices",
|
|
51
52
|
FirmwareRegistry = "FirmwareRegistry"
|
|
52
53
|
}
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -397,6 +397,22 @@ class EnergyApp {
|
|
|
397
397
|
useDeviceTest() {
|
|
398
398
|
return this.energyAppSdk.useDeviceTest();
|
|
399
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Gets the EPEX SPOT Price API for reading the cleared day-ahead
|
|
402
|
+
* wholesale electricity prices that apply to this device.
|
|
403
|
+
*
|
|
404
|
+
* These are raw market prices — excluding grid fees, levies, taxes and
|
|
405
|
+
* supplier margin, and negative when supply outruns demand — intended for
|
|
406
|
+
* energy-manager decisions such as when to charge, when to run a flexible
|
|
407
|
+
* load, and when to curtail PV feed-in. For customer-billed pricing use
|
|
408
|
+
* {@link useElectricityPrices} instead.
|
|
409
|
+
* @returns The EPEX SPOT Price API instance
|
|
410
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
411
|
+
* permission is not granted.
|
|
412
|
+
*/
|
|
413
|
+
useEpexSpotPrices() {
|
|
414
|
+
return this.energyAppSdk.useEpexSpotPrices();
|
|
415
|
+
}
|
|
400
416
|
/**
|
|
401
417
|
* Gets the current SDK version.
|
|
402
418
|
* @returns The semantic version string of the SDK
|
|
@@ -42,6 +42,7 @@ import { EnergyAppFirmwareRegistry } from "./packages/energy-app-firmware-regist
|
|
|
42
42
|
import { EnergyAppAutomation } from "./packages/energy-app-automation.cjs";
|
|
43
43
|
import { EnergyAppSavings } from "./packages/energy-app-savings.cjs";
|
|
44
44
|
import { EnergyAppDeviceTest } from "./packages/energy-app-device-test.cjs";
|
|
45
|
+
import { EnergyAppEpexSpotPrice } from "./packages/energy-app-epex-spot-price.cjs";
|
|
45
46
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
46
47
|
/**
|
|
47
48
|
* Concrete implementation of {@link EnyoEnergyAppSdk} that delegates every call
|
|
@@ -323,6 +324,20 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
323
324
|
* @returns The Device Test API instance
|
|
324
325
|
*/
|
|
325
326
|
useDeviceTest(): EnergyAppDeviceTest;
|
|
327
|
+
/**
|
|
328
|
+
* Gets the EPEX SPOT Price API for reading the cleared day-ahead
|
|
329
|
+
* wholesale electricity prices that apply to this device.
|
|
330
|
+
*
|
|
331
|
+
* These are raw market prices — excluding grid fees, levies, taxes and
|
|
332
|
+
* supplier margin, and negative when supply outruns demand — intended for
|
|
333
|
+
* energy-manager decisions such as when to charge, when to run a flexible
|
|
334
|
+
* load, and when to curtail PV feed-in. For customer-billed pricing use
|
|
335
|
+
* {@link useElectricityPrices} instead.
|
|
336
|
+
* @returns The EPEX SPOT Price API instance
|
|
337
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
338
|
+
* permission is not granted.
|
|
339
|
+
*/
|
|
340
|
+
useEpexSpotPrices(): EnergyAppEpexSpotPrice;
|
|
326
341
|
/**
|
|
327
342
|
* Gets the current SDK version.
|
|
328
343
|
* @returns The semantic version string of the SDK
|
|
@@ -41,6 +41,7 @@ import { EnergyAppFirmwareRegistry } from "./packages/energy-app-firmware-regist
|
|
|
41
41
|
import { EnergyAppAutomation } from "./packages/energy-app-automation.cjs";
|
|
42
42
|
import { EnergyAppSavings } from "./packages/energy-app-savings.cjs";
|
|
43
43
|
import { EnergyAppDeviceTest } from "./packages/energy-app-device-test.cjs";
|
|
44
|
+
import { EnergyAppEpexSpotPrice } from "./packages/energy-app-epex-spot-price.cjs";
|
|
44
45
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
45
46
|
export declare enum EnergyAppStateEnum {
|
|
46
47
|
Launching = "launching",
|
|
@@ -154,4 +155,6 @@ export interface EnyoEnergyAppSdk {
|
|
|
154
155
|
useSavings: () => EnergyAppSavings;
|
|
155
156
|
/** Get the Device Test API for answering the host's requests to test detected network devices and report whether appliances were found or created */
|
|
156
157
|
useDeviceTest: () => EnergyAppDeviceTest;
|
|
158
|
+
/** Get the EPEX SPOT Price API for reading the cleared day-ahead wholesale electricity prices that apply to this device */
|
|
159
|
+
useEpexSpotPrices: () => EnergyAppEpexSpotPrice;
|
|
157
160
|
}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -128,6 +128,8 @@ __exportStar(require("./packages/energy-app-device-test.cjs"), exports);
|
|
|
128
128
|
__exportStar(require("./implementations/device-test/device-test-validators.cjs"), exports);
|
|
129
129
|
__exportStar(require("./types/enyo-savings.cjs"), exports);
|
|
130
130
|
__exportStar(require("./packages/energy-app-savings.cjs"), exports);
|
|
131
|
+
__exportStar(require("./types/enyo-epex-spot-price.cjs"), exports);
|
|
132
|
+
__exportStar(require("./packages/energy-app-epex-spot-price.cjs"), exports);
|
|
131
133
|
__exportStar(require("./types/enyo-automation.cjs"), exports);
|
|
132
134
|
__exportStar(require("./packages/energy-app-automation.cjs"), exports);
|
|
133
135
|
__exportStar(require("./implementations/automation/automation-validators.cjs"), exports);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -112,6 +112,8 @@ export * from './packages/energy-app-device-test.cjs';
|
|
|
112
112
|
export * from './implementations/device-test/device-test-validators.cjs';
|
|
113
113
|
export * from './types/enyo-savings.cjs';
|
|
114
114
|
export * from './packages/energy-app-savings.cjs';
|
|
115
|
+
export * from './types/enyo-epex-spot-price.cjs';
|
|
116
|
+
export * from './packages/energy-app-epex-spot-price.cjs';
|
|
115
117
|
export * from './types/enyo-automation.cjs';
|
|
116
118
|
export * from './packages/energy-app-automation.cjs';
|
|
117
119
|
export * from './implementations/automation/automation-validators.cjs';
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { EnyoEpexNegativePriceWindow, EnyoEpexSpotPriceEntry, EnyoEpexSpotPriceFilter, EnyoEpexSpotPrices } from "../types/enyo-epex-spot-price.cjs";
|
|
2
|
+
/**
|
|
3
|
+
* Interface for reading EPEX SPOT day-ahead electricity prices.
|
|
4
|
+
*
|
|
5
|
+
* The host fetches the cleared prices that apply to this device and caches
|
|
6
|
+
* them locally, so every app on the device shares one source of truth instead
|
|
7
|
+
* of each one calling a price API itself. An energy manager can use the series
|
|
8
|
+
* to decide when to charge a battery, when to run a flexible load, and — via
|
|
9
|
+
* {@link EnyoInverterApplianceMetadata.blockFeedInOnNegativePrices} — when to
|
|
10
|
+
* stop exporting PV to the grid.
|
|
11
|
+
*
|
|
12
|
+
* What this API returns is **market data, not customer pricing**: the raw
|
|
13
|
+
* exchange result, excluding grid fees, levies, taxes and supplier margin, and
|
|
14
|
+
* negative whenever supply outruns demand. For what the customer is actually
|
|
15
|
+
* billed, use {@link EnergyAppEnergyPrices}; for forward-looking price
|
|
16
|
+
* forecasts published by other apps, use
|
|
17
|
+
* {@link EnergyAppDynamicPriceForecast}.
|
|
18
|
+
*
|
|
19
|
+
* Prices for the following day become known once the auction clears (14:00
|
|
20
|
+
* CET/CEST) — before that, only the current day is available, so an app must
|
|
21
|
+
* always tolerate a series that ends earlier than it would like.
|
|
22
|
+
*
|
|
23
|
+
* Access to this API requires the `EpexSpotPrices` permission
|
|
24
|
+
* ({@link EnergyAppPermissionType}); {@link EnergyApp.useEpexSpotPrices} throws
|
|
25
|
+
* when it has not been granted.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const epex = energyApp.useEpexSpotPrices();
|
|
30
|
+
*
|
|
31
|
+
* const now = await epex.getCurrentSpotPrice();
|
|
32
|
+
* if (now && now.pricePerKwh < 0) {
|
|
33
|
+
* // exporting costs money right now
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* // Re-plan whenever tomorrow's auction result arrives.
|
|
37
|
+
* epex.onSpotPricesUpdated(prices => scheduler.replan(prices.entries));
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export interface EnergyAppEpexSpotPrice {
|
|
41
|
+
/**
|
|
42
|
+
* Retrieves the cached EPEX SPOT price series for a time range.
|
|
43
|
+
*
|
|
44
|
+
* Returns `null` when the host holds no prices at all (for example on a
|
|
45
|
+
* device that has never been online). A series that is merely shorter than
|
|
46
|
+
* the requested range is returned as-is with the entries that are known —
|
|
47
|
+
* check the last entry's `endTimestampIso` before planning against it.
|
|
48
|
+
*
|
|
49
|
+
* @param filter - Optional time range. Defaults to the current delivery
|
|
50
|
+
* period through the end of the known series.
|
|
51
|
+
* @returns Promise resolving to the price series, or `null` if none is available.
|
|
52
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
53
|
+
* permission is not granted.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const prices = await epex.getSpotPrices({
|
|
58
|
+
* fromIso: '2026-08-12T00:00:00Z',
|
|
59
|
+
* untilIso: '2026-08-13T00:00:00Z'
|
|
60
|
+
* });
|
|
61
|
+
* const cheapest = prices?.entries
|
|
62
|
+
* .reduce((min, e) => e.pricePerKwh < min.pricePerKwh ? e : min);
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
getSpotPrices(filter?: EnyoEpexSpotPriceFilter): Promise<EnyoEpexSpotPrices | null>;
|
|
66
|
+
/**
|
|
67
|
+
* Retrieves the price of the delivery period that contains "now".
|
|
68
|
+
*
|
|
69
|
+
* Convenience wrapper around {@link getSpotPrices} for control loops that
|
|
70
|
+
* only need the price they are currently exposed to.
|
|
71
|
+
*
|
|
72
|
+
* @returns Promise resolving to the current entry, or `null` when the
|
|
73
|
+
* current period is not covered by the cached series.
|
|
74
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
75
|
+
* permission is not granted.
|
|
76
|
+
*/
|
|
77
|
+
getCurrentSpotPrice(): Promise<EnyoEpexSpotPriceEntry | null>;
|
|
78
|
+
/**
|
|
79
|
+
* Retrieves the contiguous runs of delivery periods priced below zero
|
|
80
|
+
* within the requested range.
|
|
81
|
+
*
|
|
82
|
+
* This is the same information as {@link getSpotPrices}, pre-grouped for
|
|
83
|
+
* the decisions that care about it: curtailing PV feed-in, and pulling
|
|
84
|
+
* consumption into hours the market is paying for. Windows are returned in
|
|
85
|
+
* chronological order; an empty array means no negative prices are known
|
|
86
|
+
* for the range.
|
|
87
|
+
*
|
|
88
|
+
* @param filter - Optional time range; same defaults as {@link getSpotPrices}.
|
|
89
|
+
* @returns Promise resolving to the negative-price windows in the range.
|
|
90
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
91
|
+
* permission is not granted.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const windows = await epex.getNegativePriceWindows();
|
|
96
|
+
* for (const w of windows) {
|
|
97
|
+
* console.log(`negative from ${w.startIso} for ${w.durationMinutes} min`);
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
getNegativePriceWindows(filter?: EnyoEpexSpotPriceFilter): Promise<EnyoEpexNegativePriceWindow[]>;
|
|
102
|
+
/**
|
|
103
|
+
* Registers a listener invoked whenever the host has refreshed the cached
|
|
104
|
+
* series — most importantly when the day-ahead auction for the next day
|
|
105
|
+
* clears, which is the moment a scheduler can plan a full 24 hours ahead.
|
|
106
|
+
*
|
|
107
|
+
* The listener receives the complete refreshed series, not just the
|
|
108
|
+
* changed entries.
|
|
109
|
+
*
|
|
110
|
+
* @param listener - Callback invoked with the newly cached price series.
|
|
111
|
+
* @returns A unique listener id for {@link offSpotPricesUpdated}.
|
|
112
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
113
|
+
* permission is not granted.
|
|
114
|
+
*/
|
|
115
|
+
onSpotPricesUpdated(listener: (prices: EnyoEpexSpotPrices) => void | Promise<void>): string;
|
|
116
|
+
/**
|
|
117
|
+
* Removes a previously registered price-update listener. Unknown listener
|
|
118
|
+
* ids are ignored.
|
|
119
|
+
*
|
|
120
|
+
* @param listenerId - The id returned from {@link onSpotPricesUpdated}.
|
|
121
|
+
*/
|
|
122
|
+
offSpotPricesUpdated(listenerId: string): void;
|
|
123
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EnyoPowerSourceEnum = exports.EnyoHeatpumpControlPurposeEnum = exports.EnyoChargingProfileTypeEnum = exports.EnyoCommandAcknowledgeAnswerEnum = exports.EnyoStorageControlDirectionEnum = exports.EnyoStorageControlModeEnum = exports.EnyoStorageScheduleDirectionEnum = exports.EnyoStorageScheduleModeEnum = exports.EnyoDataBusMessageEnum = exports.EnyoChargeInitiatorEnum = exports.EnyoChargeModeEnum = exports.EnyoChargingStopReason = exports.EnyoChargingMeterValueContext = exports.EnyoStringStateEnum = exports.EnyoInverterStateEnum = exports.EnyoBatteryStateEnum = exports.EnyoGridOperatorLimitTypeEnum = exports.EnyoDataBusCommandReasonCategoryEnum = exports.EnyoDataBusCommandReasonTypeEnum = void 0;
|
|
3
|
+
exports.EnyoPowerSourceEnum = exports.EnyoHeatpumpControlPurposeEnum = exports.EnyoChargingProfileTypeEnum = exports.EnyoCommandAcknowledgeAnswerEnum = exports.EnyoStorageControlDirectionEnum = exports.EnyoStorageControlModeEnum = exports.EnyoStorageScheduleDirectionEnum = exports.EnyoStorageScheduleModeEnum = exports.EnyoDataBusMessageEnum = exports.EnyoChargeInitiatorEnum = exports.EnyoChargeModeEnum = exports.EnyoChargingStopReason = exports.EnyoChargingMeterValueContext = exports.EnyoStringStateEnum = exports.EnyoHeatingRodStateEnum = exports.EnyoInverterStateEnum = exports.EnyoBatteryStateEnum = exports.EnyoGridOperatorLimitTypeEnum = exports.EnyoDataBusCommandReasonCategoryEnum = exports.EnyoDataBusCommandReasonTypeEnum = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Enum representing the reason type for why a data bus command was issued.
|
|
6
6
|
* Used to attach context to commands for logging, debugging, and UI display.
|
|
@@ -100,6 +100,22 @@ var EnyoInverterStateEnum;
|
|
|
100
100
|
EnyoInverterStateEnum["Fault"] = "fault";
|
|
101
101
|
EnyoInverterStateEnum["Standby"] = "standby";
|
|
102
102
|
})(EnyoInverterStateEnum || (exports.EnyoInverterStateEnum = EnyoInverterStateEnum = {}));
|
|
103
|
+
/**
|
|
104
|
+
* Operating state of a heating rod as reported on the data bus via
|
|
105
|
+
* {@link EnyoDataBusHeatingRodValuesV1}.
|
|
106
|
+
*
|
|
107
|
+
* This is the *observed* state of the element, not the configured mode: the
|
|
108
|
+
* appliance metadata's {@link EnyoHeatingRodApplianceModeEnum} describes how the
|
|
109
|
+
* heating rod is set up, while this enum says whether it is drawing power right
|
|
110
|
+
* now.
|
|
111
|
+
*/
|
|
112
|
+
var EnyoHeatingRodStateEnum;
|
|
113
|
+
(function (EnyoHeatingRodStateEnum) {
|
|
114
|
+
/** The heating element is not energized and consumes (close to) no power */
|
|
115
|
+
EnyoHeatingRodStateEnum["Idle"] = "idle";
|
|
116
|
+
/** The heating element is energized and heating */
|
|
117
|
+
EnyoHeatingRodStateEnum["Running"] = "running";
|
|
118
|
+
})(EnyoHeatingRodStateEnum || (exports.EnyoHeatingRodStateEnum = EnyoHeatingRodStateEnum = {}));
|
|
103
119
|
var EnyoStringStateEnum;
|
|
104
120
|
(function (EnyoStringStateEnum) {
|
|
105
121
|
EnyoStringStateEnum["Off"] = "off";
|
|
@@ -180,6 +196,7 @@ var EnyoDataBusMessageEnum;
|
|
|
180
196
|
EnyoDataBusMessageEnum["ApplianceFlexibilityAnnouncementV1"] = "ApplianceFlexibilityAnnouncementV1";
|
|
181
197
|
EnyoDataBusMessageEnum["ApplianceStateUpdateV1"] = "ApplianceStateUpdateV1";
|
|
182
198
|
EnyoDataBusMessageEnum["HeatpumpValuesUpdateV1"] = "HeatpumpValuesUpdateV1";
|
|
199
|
+
EnyoDataBusMessageEnum["HeatingRodValuesUpdateV1"] = "HeatingRodValuesUpdateV1";
|
|
183
200
|
EnyoDataBusMessageEnum["ChargingStartedV1"] = "ChargingStartedV1";
|
|
184
201
|
EnyoDataBusMessageEnum["ChargingMeterValuesUpdateV1"] = "ChargingMeterValuesUpdateV1";
|
|
185
202
|
EnyoDataBusMessageEnum["ChargingStoppedV1"] = "ChargingStoppedV1";
|
|
@@ -153,6 +153,21 @@ export declare enum EnyoInverterStateEnum {
|
|
|
153
153
|
Fault = "fault",
|
|
154
154
|
Standby = "standby"
|
|
155
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Operating state of a heating rod as reported on the data bus via
|
|
158
|
+
* {@link EnyoDataBusHeatingRodValuesV1}.
|
|
159
|
+
*
|
|
160
|
+
* This is the *observed* state of the element, not the configured mode: the
|
|
161
|
+
* appliance metadata's {@link EnyoHeatingRodApplianceModeEnum} describes how the
|
|
162
|
+
* heating rod is set up, while this enum says whether it is drawing power right
|
|
163
|
+
* now.
|
|
164
|
+
*/
|
|
165
|
+
export declare enum EnyoHeatingRodStateEnum {
|
|
166
|
+
/** The heating element is not energized and consumes (close to) no power */
|
|
167
|
+
Idle = "idle",
|
|
168
|
+
/** The heating element is energized and heating */
|
|
169
|
+
Running = "running"
|
|
170
|
+
}
|
|
156
171
|
export declare enum EnyoStringStateEnum {
|
|
157
172
|
Off = "off",
|
|
158
173
|
Sleeping = "sleeping",
|
|
@@ -235,6 +250,7 @@ export declare enum EnyoDataBusMessageEnum {
|
|
|
235
250
|
ApplianceFlexibilityAnnouncementV1 = "ApplianceFlexibilityAnnouncementV1",
|
|
236
251
|
ApplianceStateUpdateV1 = "ApplianceStateUpdateV1",
|
|
237
252
|
HeatpumpValuesUpdateV1 = "HeatpumpValuesUpdateV1",
|
|
253
|
+
HeatingRodValuesUpdateV1 = "HeatingRodValuesUpdateV1",
|
|
238
254
|
ChargingStartedV1 = "ChargingStartedV1",
|
|
239
255
|
ChargingMeterValuesUpdateV1 = "ChargingMeterValuesUpdateV1",
|
|
240
256
|
ChargingStoppedV1 = "ChargingStoppedV1",
|
|
@@ -418,6 +434,34 @@ export interface EnyoDataBusHeatpumpValuesV1 extends EnyoDataBusMessage {
|
|
|
418
434
|
gridOperatorLimit?: EnyoGridOperatorLimit;
|
|
419
435
|
};
|
|
420
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Live values of a heating rod (immersion / electric resistive heating
|
|
439
|
+
* element), published by the integration that owns the appliance.
|
|
440
|
+
*
|
|
441
|
+
* Send this whenever the power draw or the operating state changes, so an
|
|
442
|
+
* energy manager can account for the load and decide how much power to
|
|
443
|
+
* announce back via {@link EnyoDataBusSetHeatingRodAvailablePowerV2}.
|
|
444
|
+
*/
|
|
445
|
+
export interface EnyoDataBusHeatingRodValuesV1 extends EnyoDataBusMessage {
|
|
446
|
+
type: 'message';
|
|
447
|
+
message: EnyoDataBusMessageEnum.HeatingRodValuesUpdateV1;
|
|
448
|
+
/** ID of the appliance that delivered these values */
|
|
449
|
+
applianceId: string;
|
|
450
|
+
data: {
|
|
451
|
+
/**
|
|
452
|
+
* Current electrical power consumption of the heating rod in Watt.
|
|
453
|
+
* Always positive (a heating rod only consumes). Omit when the
|
|
454
|
+
* integration cannot measure it — do not send `0` as a stand-in for
|
|
455
|
+
* "unknown", since a consumer cannot tell that apart from "off".
|
|
456
|
+
*/
|
|
457
|
+
powerW?: number;
|
|
458
|
+
/**
|
|
459
|
+
* Current operating state of the heating element. Omit when the
|
|
460
|
+
* integration cannot determine it.
|
|
461
|
+
*/
|
|
462
|
+
state?: EnyoHeatingRodStateEnum;
|
|
463
|
+
};
|
|
464
|
+
}
|
|
421
465
|
export interface EnyoDataBusBatteryValuesUpdateV1 extends EnyoDataBusMessage {
|
|
422
466
|
type: 'message';
|
|
423
467
|
message: EnyoDataBusMessageEnum.BatteryValuesUpdateV1;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { EnyoCurrencyEnum } from "./enyo-currency.cjs";
|
|
2
|
+
import { ForecastResolutionEnum } from "./enyo-forecasting.cjs";
|
|
3
|
+
/**
|
|
4
|
+
* A single cleared market price for one delivery period.
|
|
5
|
+
*
|
|
6
|
+
* Prices are the raw exchange result: they exclude grid fees, levies, taxes
|
|
7
|
+
* and any supplier margin, and they **can be negative** when generation
|
|
8
|
+
* exceeds demand.
|
|
9
|
+
*/
|
|
10
|
+
export interface EnyoEpexSpotPriceEntry {
|
|
11
|
+
/** Start of the delivery period in ISO format (inclusive) */
|
|
12
|
+
timestampIso: string;
|
|
13
|
+
/** End of the delivery period in ISO format (exclusive) */
|
|
14
|
+
endTimestampIso: string;
|
|
15
|
+
/**
|
|
16
|
+
* Cleared price per megawatt hour, in the series' currency. This is the
|
|
17
|
+
* unit the exchange publishes (e.g. `-14.2` for EUR -14.20/MWh).
|
|
18
|
+
*/
|
|
19
|
+
pricePerMwh: number;
|
|
20
|
+
/**
|
|
21
|
+
* The same price expressed per kilowatt hour (`pricePerMwh / 1000`), for
|
|
22
|
+
* consistency with the rest of the SDK, which prices energy per kWh.
|
|
23
|
+
*/
|
|
24
|
+
pricePerKwh: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A contiguous run of delivery periods whose spot price is below zero.
|
|
28
|
+
*
|
|
29
|
+
* Convenience shape for the common decision "should PV feed-in be curtailed /
|
|
30
|
+
* should a flexible load be pulled forward?" — see
|
|
31
|
+
* {@link EnyoInverterApplianceMetadata.blockFeedInOnNegativePrices}.
|
|
32
|
+
*/
|
|
33
|
+
export interface EnyoEpexNegativePriceWindow {
|
|
34
|
+
/** Start of the window in ISO format (inclusive) */
|
|
35
|
+
startIso: string;
|
|
36
|
+
/** End of the window in ISO format (exclusive) */
|
|
37
|
+
endIso: string;
|
|
38
|
+
/** Length of the window in minutes */
|
|
39
|
+
durationMinutes: number;
|
|
40
|
+
/** Most negative price per kWh observed inside the window */
|
|
41
|
+
minPricePerKwh: number;
|
|
42
|
+
/** Arithmetic mean price per kWh across the window's delivery periods */
|
|
43
|
+
averagePricePerKwh: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The EPEX SPOT day-ahead price series that applies to this device.
|
|
47
|
+
*/
|
|
48
|
+
export interface EnyoEpexSpotPrices {
|
|
49
|
+
/** Currency of every price in {@link entries} */
|
|
50
|
+
currency: EnyoCurrencyEnum;
|
|
51
|
+
/**
|
|
52
|
+
* Length of one delivery period. EPEX SPOT day-ahead moved to 15-minute
|
|
53
|
+
* periods in 2025, but older data is still hourly — read this instead of
|
|
54
|
+
* assuming a resolution.
|
|
55
|
+
*/
|
|
56
|
+
resolution: ForecastResolutionEnum;
|
|
57
|
+
/**
|
|
58
|
+
* When the host last fetched this series from the exchange, in ISO format.
|
|
59
|
+
* Useful to detect stale data when the device was offline.
|
|
60
|
+
*/
|
|
61
|
+
retrievedAtIso: string;
|
|
62
|
+
/** Price entries sorted ascending by `timestampIso`, without gaps */
|
|
63
|
+
entries: EnyoEpexSpotPriceEntry[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Restricts a price query to a time range.
|
|
67
|
+
*
|
|
68
|
+
* Both fields are optional: with an empty filter the host returns the full
|
|
69
|
+
* currently known window — typically today plus tomorrow, once the day-ahead
|
|
70
|
+
* auction has cleared.
|
|
71
|
+
*/
|
|
72
|
+
export interface EnyoEpexSpotPriceFilter {
|
|
73
|
+
/** Start of the requested range in ISO format (inclusive). Defaults to the start of the current delivery period. */
|
|
74
|
+
fromIso?: string;
|
|
75
|
+
/** End of the requested range in ISO format (exclusive). Defaults to the end of the known series. */
|
|
76
|
+
untilIso?: string;
|
|
77
|
+
}
|
|
@@ -27,4 +27,19 @@ export interface EnyoInverterApplianceMetadata {
|
|
|
27
27
|
yearBuilt?: number;
|
|
28
28
|
/** Groups of PV modules attached to this inverter, each with its own orientation */
|
|
29
29
|
moduleGroups?: EnyoInverterModuleGroup[];
|
|
30
|
+
/**
|
|
31
|
+
* Whether grid feed-in of this inverter should be blocked while the
|
|
32
|
+
* electricity price is negative.
|
|
33
|
+
*
|
|
34
|
+
* This is a user/installer configuration flag, not a live state: it
|
|
35
|
+
* expresses the intent that during negative market prices (see
|
|
36
|
+
* {@link EnergyAppEpexSpotPrice}) the inverter's export to the grid should
|
|
37
|
+
* be curtailed to 0 W, because feeding in costs money instead of earning
|
|
38
|
+
* it. Whoever controls the inverter — typically the energy manager sending
|
|
39
|
+
* `SetInverterFeedInLimitV1` — is responsible for honoring the flag and for
|
|
40
|
+
* lifting the curtailment once prices turn positive again.
|
|
41
|
+
*
|
|
42
|
+
* `undefined` means "not configured" and should be treated as `false`.
|
|
43
|
+
*/
|
|
44
|
+
blockFeedInOnNegativePrices?: boolean;
|
|
30
45
|
}
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
|
|
|
9
9
|
/**
|
|
10
10
|
* Current version of the enyo Energy App SDK.
|
|
11
11
|
*/
|
|
12
|
-
exports.SDK_VERSION = '0.0.
|
|
12
|
+
exports.SDK_VERSION = '0.0.186';
|
|
13
13
|
/**
|
|
14
14
|
* Gets the current SDK version.
|
|
15
15
|
* @returns The semantic version string of the SDK
|
package/dist/cjs/version.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type EnergyAppPermissionType = 'RestrictedInternetAccess' | 'NetworkDeviceDiscovery' | 'NetworkDeviceSearch' | 'NetworkDeviceAccess' | 'AllNetworkDeviceAccess' | 'Modbus' | 'Storage' | 'Appliance' | 'AllAppliances' | 'SendDataBusValues' | 'SubscribeDataBus' | 'SendDataBusCommands' | 'OcppServer' | 'ChargingCard' | 'Vehicle' | 'Charge' | 'SecretManager' | 'LocationZipCode' | 'LocationCoordinates' | 'Timeseries' | 'EnergyManagerInfo' | 'ElectricityTariff' | 'WeatherForecastRegister' | 'WeatherForecastUse' | 'PvForecastRegister' | 'PvForecastUse' | 'DynamicPriceForecastRegister' | 'DynamicPriceForecastUse' | 'PvSystemRegister' | 'PvSystemUse' | 'InverterControlCommands' | 'BatteryControlCommands' | 'BatteryStorageState' | 'ChargerControlCommands' | 'ModbusRtu' | 'EnergyPrices' | 'EnergyManager' | 'EebusDeviceManagement' | 'EebusDataAccess' | 'EebusControl' | 'Mqtt' | 'Bluetooth' | 'Wifi' | 'ChildProcess' | 'Udp' | 'ProvidedFiles' | 'Automation' | 'Savings' | 'FirmwareRegistry';
|
|
1
|
+
export type EnergyAppPermissionType = 'RestrictedInternetAccess' | 'NetworkDeviceDiscovery' | 'NetworkDeviceSearch' | 'NetworkDeviceAccess' | 'AllNetworkDeviceAccess' | 'Modbus' | 'Storage' | 'Appliance' | 'AllAppliances' | 'SendDataBusValues' | 'SubscribeDataBus' | 'SendDataBusCommands' | 'OcppServer' | 'ChargingCard' | 'Vehicle' | 'Charge' | 'SecretManager' | 'LocationZipCode' | 'LocationCoordinates' | 'Timeseries' | 'EnergyManagerInfo' | 'ElectricityTariff' | 'WeatherForecastRegister' | 'WeatherForecastUse' | 'PvForecastRegister' | 'PvForecastUse' | 'DynamicPriceForecastRegister' | 'DynamicPriceForecastUse' | 'PvSystemRegister' | 'PvSystemUse' | 'InverterControlCommands' | 'BatteryControlCommands' | 'BatteryStorageState' | 'ChargerControlCommands' | 'ModbusRtu' | 'EnergyPrices' | 'EnergyManager' | 'EebusDeviceManagement' | 'EebusDataAccess' | 'EebusControl' | 'Mqtt' | 'Bluetooth' | 'Wifi' | 'ChildProcess' | 'Udp' | 'ProvidedFiles' | 'Automation' | 'Savings' | 'EpexSpotPrices' | 'FirmwareRegistry';
|
|
2
2
|
export declare enum EnergyAppPermissionTypeEnum {
|
|
3
3
|
RestrictedInternetAccess = "RestrictedInternetAccess",
|
|
4
4
|
NetworkDeviceDiscovery = "NetworkDeviceDiscovery",
|
|
@@ -48,5 +48,6 @@ export declare enum EnergyAppPermissionTypeEnum {
|
|
|
48
48
|
ProvidedFiles = "ProvidedFiles",
|
|
49
49
|
Automation = "Automation",
|
|
50
50
|
Savings = "Savings",
|
|
51
|
+
EpexSpotPrices = "EpexSpotPrices",
|
|
51
52
|
FirmwareRegistry = "FirmwareRegistry"
|
|
52
53
|
}
|
|
@@ -48,5 +48,6 @@ export var EnergyAppPermissionTypeEnum;
|
|
|
48
48
|
EnergyAppPermissionTypeEnum["ProvidedFiles"] = "ProvidedFiles";
|
|
49
49
|
EnergyAppPermissionTypeEnum["Automation"] = "Automation";
|
|
50
50
|
EnergyAppPermissionTypeEnum["Savings"] = "Savings";
|
|
51
|
+
EnergyAppPermissionTypeEnum["EpexSpotPrices"] = "EpexSpotPrices";
|
|
51
52
|
EnergyAppPermissionTypeEnum["FirmwareRegistry"] = "FirmwareRegistry";
|
|
52
53
|
})(EnergyAppPermissionTypeEnum || (EnergyAppPermissionTypeEnum = {}));
|
package/dist/energy-app.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ import { EnergyAppFirmwareRegistry } from "./packages/energy-app-firmware-regist
|
|
|
42
42
|
import { EnergyAppAutomation } from "./packages/energy-app-automation.js";
|
|
43
43
|
import { EnergyAppSavings } from "./packages/energy-app-savings.js";
|
|
44
44
|
import { EnergyAppDeviceTest } from "./packages/energy-app-device-test.js";
|
|
45
|
+
import { EnergyAppEpexSpotPrice } from "./packages/energy-app-epex-spot-price.js";
|
|
45
46
|
import { UseFetchOptions } from "./types/enyo-fetch.js";
|
|
46
47
|
/**
|
|
47
48
|
* Concrete implementation of {@link EnyoEnergyAppSdk} that delegates every call
|
|
@@ -323,6 +324,20 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
323
324
|
* @returns The Device Test API instance
|
|
324
325
|
*/
|
|
325
326
|
useDeviceTest(): EnergyAppDeviceTest;
|
|
327
|
+
/**
|
|
328
|
+
* Gets the EPEX SPOT Price API for reading the cleared day-ahead
|
|
329
|
+
* wholesale electricity prices that apply to this device.
|
|
330
|
+
*
|
|
331
|
+
* These are raw market prices — excluding grid fees, levies, taxes and
|
|
332
|
+
* supplier margin, and negative when supply outruns demand — intended for
|
|
333
|
+
* energy-manager decisions such as when to charge, when to run a flexible
|
|
334
|
+
* load, and when to curtail PV feed-in. For customer-billed pricing use
|
|
335
|
+
* {@link useElectricityPrices} instead.
|
|
336
|
+
* @returns The EPEX SPOT Price API instance
|
|
337
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
338
|
+
* permission is not granted.
|
|
339
|
+
*/
|
|
340
|
+
useEpexSpotPrices(): EnergyAppEpexSpotPrice;
|
|
326
341
|
/**
|
|
327
342
|
* Gets the current SDK version.
|
|
328
343
|
* @returns The semantic version string of the SDK
|
package/dist/energy-app.js
CHANGED
|
@@ -394,6 +394,22 @@ export class EnergyApp {
|
|
|
394
394
|
useDeviceTest() {
|
|
395
395
|
return this.energyAppSdk.useDeviceTest();
|
|
396
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Gets the EPEX SPOT Price API for reading the cleared day-ahead
|
|
399
|
+
* wholesale electricity prices that apply to this device.
|
|
400
|
+
*
|
|
401
|
+
* These are raw market prices — excluding grid fees, levies, taxes and
|
|
402
|
+
* supplier margin, and negative when supply outruns demand — intended for
|
|
403
|
+
* energy-manager decisions such as when to charge, when to run a flexible
|
|
404
|
+
* load, and when to curtail PV feed-in. For customer-billed pricing use
|
|
405
|
+
* {@link useElectricityPrices} instead.
|
|
406
|
+
* @returns The EPEX SPOT Price API instance
|
|
407
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
408
|
+
* permission is not granted.
|
|
409
|
+
*/
|
|
410
|
+
useEpexSpotPrices() {
|
|
411
|
+
return this.energyAppSdk.useEpexSpotPrices();
|
|
412
|
+
}
|
|
397
413
|
/**
|
|
398
414
|
* Gets the current SDK version.
|
|
399
415
|
* @returns The semantic version string of the SDK
|
|
@@ -41,6 +41,7 @@ import { EnergyAppFirmwareRegistry } from "./packages/energy-app-firmware-regist
|
|
|
41
41
|
import { EnergyAppAutomation } from "./packages/energy-app-automation.js";
|
|
42
42
|
import { EnergyAppSavings } from "./packages/energy-app-savings.js";
|
|
43
43
|
import { EnergyAppDeviceTest } from "./packages/energy-app-device-test.js";
|
|
44
|
+
import { EnergyAppEpexSpotPrice } from "./packages/energy-app-epex-spot-price.js";
|
|
44
45
|
import { UseFetchOptions } from "./types/enyo-fetch.js";
|
|
45
46
|
export declare enum EnergyAppStateEnum {
|
|
46
47
|
Launching = "launching",
|
|
@@ -154,4 +155,6 @@ export interface EnyoEnergyAppSdk {
|
|
|
154
155
|
useSavings: () => EnergyAppSavings;
|
|
155
156
|
/** Get the Device Test API for answering the host's requests to test detected network devices and report whether appliances were found or created */
|
|
156
157
|
useDeviceTest: () => EnergyAppDeviceTest;
|
|
158
|
+
/** Get the EPEX SPOT Price API for reading the cleared day-ahead wholesale electricity prices that apply to this device */
|
|
159
|
+
useEpexSpotPrices: () => EnergyAppEpexSpotPrice;
|
|
157
160
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -112,6 +112,8 @@ export * from './packages/energy-app-device-test.js';
|
|
|
112
112
|
export * from './implementations/device-test/device-test-validators.js';
|
|
113
113
|
export * from './types/enyo-savings.js';
|
|
114
114
|
export * from './packages/energy-app-savings.js';
|
|
115
|
+
export * from './types/enyo-epex-spot-price.js';
|
|
116
|
+
export * from './packages/energy-app-epex-spot-price.js';
|
|
115
117
|
export * from './types/enyo-automation.js';
|
|
116
118
|
export * from './packages/energy-app-automation.js';
|
|
117
119
|
export * from './implementations/automation/automation-validators.js';
|
package/dist/index.js
CHANGED
|
@@ -112,6 +112,8 @@ export * from './packages/energy-app-device-test.js';
|
|
|
112
112
|
export * from './implementations/device-test/device-test-validators.js';
|
|
113
113
|
export * from './types/enyo-savings.js';
|
|
114
114
|
export * from './packages/energy-app-savings.js';
|
|
115
|
+
export * from './types/enyo-epex-spot-price.js';
|
|
116
|
+
export * from './packages/energy-app-epex-spot-price.js';
|
|
115
117
|
export * from './types/enyo-automation.js';
|
|
116
118
|
export * from './packages/energy-app-automation.js';
|
|
117
119
|
export * from './implementations/automation/automation-validators.js';
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { EnyoEpexNegativePriceWindow, EnyoEpexSpotPriceEntry, EnyoEpexSpotPriceFilter, EnyoEpexSpotPrices } from "../types/enyo-epex-spot-price.js";
|
|
2
|
+
/**
|
|
3
|
+
* Interface for reading EPEX SPOT day-ahead electricity prices.
|
|
4
|
+
*
|
|
5
|
+
* The host fetches the cleared prices that apply to this device and caches
|
|
6
|
+
* them locally, so every app on the device shares one source of truth instead
|
|
7
|
+
* of each one calling a price API itself. An energy manager can use the series
|
|
8
|
+
* to decide when to charge a battery, when to run a flexible load, and — via
|
|
9
|
+
* {@link EnyoInverterApplianceMetadata.blockFeedInOnNegativePrices} — when to
|
|
10
|
+
* stop exporting PV to the grid.
|
|
11
|
+
*
|
|
12
|
+
* What this API returns is **market data, not customer pricing**: the raw
|
|
13
|
+
* exchange result, excluding grid fees, levies, taxes and supplier margin, and
|
|
14
|
+
* negative whenever supply outruns demand. For what the customer is actually
|
|
15
|
+
* billed, use {@link EnergyAppEnergyPrices}; for forward-looking price
|
|
16
|
+
* forecasts published by other apps, use
|
|
17
|
+
* {@link EnergyAppDynamicPriceForecast}.
|
|
18
|
+
*
|
|
19
|
+
* Prices for the following day become known once the auction clears (14:00
|
|
20
|
+
* CET/CEST) — before that, only the current day is available, so an app must
|
|
21
|
+
* always tolerate a series that ends earlier than it would like.
|
|
22
|
+
*
|
|
23
|
+
* Access to this API requires the `EpexSpotPrices` permission
|
|
24
|
+
* ({@link EnergyAppPermissionType}); {@link EnergyApp.useEpexSpotPrices} throws
|
|
25
|
+
* when it has not been granted.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const epex = energyApp.useEpexSpotPrices();
|
|
30
|
+
*
|
|
31
|
+
* const now = await epex.getCurrentSpotPrice();
|
|
32
|
+
* if (now && now.pricePerKwh < 0) {
|
|
33
|
+
* // exporting costs money right now
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* // Re-plan whenever tomorrow's auction result arrives.
|
|
37
|
+
* epex.onSpotPricesUpdated(prices => scheduler.replan(prices.entries));
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export interface EnergyAppEpexSpotPrice {
|
|
41
|
+
/**
|
|
42
|
+
* Retrieves the cached EPEX SPOT price series for a time range.
|
|
43
|
+
*
|
|
44
|
+
* Returns `null` when the host holds no prices at all (for example on a
|
|
45
|
+
* device that has never been online). A series that is merely shorter than
|
|
46
|
+
* the requested range is returned as-is with the entries that are known —
|
|
47
|
+
* check the last entry's `endTimestampIso` before planning against it.
|
|
48
|
+
*
|
|
49
|
+
* @param filter - Optional time range. Defaults to the current delivery
|
|
50
|
+
* period through the end of the known series.
|
|
51
|
+
* @returns Promise resolving to the price series, or `null` if none is available.
|
|
52
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
53
|
+
* permission is not granted.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const prices = await epex.getSpotPrices({
|
|
58
|
+
* fromIso: '2026-08-12T00:00:00Z',
|
|
59
|
+
* untilIso: '2026-08-13T00:00:00Z'
|
|
60
|
+
* });
|
|
61
|
+
* const cheapest = prices?.entries
|
|
62
|
+
* .reduce((min, e) => e.pricePerKwh < min.pricePerKwh ? e : min);
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
getSpotPrices(filter?: EnyoEpexSpotPriceFilter): Promise<EnyoEpexSpotPrices | null>;
|
|
66
|
+
/**
|
|
67
|
+
* Retrieves the price of the delivery period that contains "now".
|
|
68
|
+
*
|
|
69
|
+
* Convenience wrapper around {@link getSpotPrices} for control loops that
|
|
70
|
+
* only need the price they are currently exposed to.
|
|
71
|
+
*
|
|
72
|
+
* @returns Promise resolving to the current entry, or `null` when the
|
|
73
|
+
* current period is not covered by the cached series.
|
|
74
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
75
|
+
* permission is not granted.
|
|
76
|
+
*/
|
|
77
|
+
getCurrentSpotPrice(): Promise<EnyoEpexSpotPriceEntry | null>;
|
|
78
|
+
/**
|
|
79
|
+
* Retrieves the contiguous runs of delivery periods priced below zero
|
|
80
|
+
* within the requested range.
|
|
81
|
+
*
|
|
82
|
+
* This is the same information as {@link getSpotPrices}, pre-grouped for
|
|
83
|
+
* the decisions that care about it: curtailing PV feed-in, and pulling
|
|
84
|
+
* consumption into hours the market is paying for. Windows are returned in
|
|
85
|
+
* chronological order; an empty array means no negative prices are known
|
|
86
|
+
* for the range.
|
|
87
|
+
*
|
|
88
|
+
* @param filter - Optional time range; same defaults as {@link getSpotPrices}.
|
|
89
|
+
* @returns Promise resolving to the negative-price windows in the range.
|
|
90
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
91
|
+
* permission is not granted.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const windows = await epex.getNegativePriceWindows();
|
|
96
|
+
* for (const w of windows) {
|
|
97
|
+
* console.log(`negative from ${w.startIso} for ${w.durationMinutes} min`);
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
getNegativePriceWindows(filter?: EnyoEpexSpotPriceFilter): Promise<EnyoEpexNegativePriceWindow[]>;
|
|
102
|
+
/**
|
|
103
|
+
* Registers a listener invoked whenever the host has refreshed the cached
|
|
104
|
+
* series — most importantly when the day-ahead auction for the next day
|
|
105
|
+
* clears, which is the moment a scheduler can plan a full 24 hours ahead.
|
|
106
|
+
*
|
|
107
|
+
* The listener receives the complete refreshed series, not just the
|
|
108
|
+
* changed entries.
|
|
109
|
+
*
|
|
110
|
+
* @param listener - Callback invoked with the newly cached price series.
|
|
111
|
+
* @returns A unique listener id for {@link offSpotPricesUpdated}.
|
|
112
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EpexSpotPrices`
|
|
113
|
+
* permission is not granted.
|
|
114
|
+
*/
|
|
115
|
+
onSpotPricesUpdated(listener: (prices: EnyoEpexSpotPrices) => void | Promise<void>): string;
|
|
116
|
+
/**
|
|
117
|
+
* Removes a previously registered price-update listener. Unknown listener
|
|
118
|
+
* ids are ignored.
|
|
119
|
+
*
|
|
120
|
+
* @param listenerId - The id returned from {@link onSpotPricesUpdated}.
|
|
121
|
+
*/
|
|
122
|
+
offSpotPricesUpdated(listenerId: string): void;
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -153,6 +153,21 @@ export declare enum EnyoInverterStateEnum {
|
|
|
153
153
|
Fault = "fault",
|
|
154
154
|
Standby = "standby"
|
|
155
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Operating state of a heating rod as reported on the data bus via
|
|
158
|
+
* {@link EnyoDataBusHeatingRodValuesV1}.
|
|
159
|
+
*
|
|
160
|
+
* This is the *observed* state of the element, not the configured mode: the
|
|
161
|
+
* appliance metadata's {@link EnyoHeatingRodApplianceModeEnum} describes how the
|
|
162
|
+
* heating rod is set up, while this enum says whether it is drawing power right
|
|
163
|
+
* now.
|
|
164
|
+
*/
|
|
165
|
+
export declare enum EnyoHeatingRodStateEnum {
|
|
166
|
+
/** The heating element is not energized and consumes (close to) no power */
|
|
167
|
+
Idle = "idle",
|
|
168
|
+
/** The heating element is energized and heating */
|
|
169
|
+
Running = "running"
|
|
170
|
+
}
|
|
156
171
|
export declare enum EnyoStringStateEnum {
|
|
157
172
|
Off = "off",
|
|
158
173
|
Sleeping = "sleeping",
|
|
@@ -235,6 +250,7 @@ export declare enum EnyoDataBusMessageEnum {
|
|
|
235
250
|
ApplianceFlexibilityAnnouncementV1 = "ApplianceFlexibilityAnnouncementV1",
|
|
236
251
|
ApplianceStateUpdateV1 = "ApplianceStateUpdateV1",
|
|
237
252
|
HeatpumpValuesUpdateV1 = "HeatpumpValuesUpdateV1",
|
|
253
|
+
HeatingRodValuesUpdateV1 = "HeatingRodValuesUpdateV1",
|
|
238
254
|
ChargingStartedV1 = "ChargingStartedV1",
|
|
239
255
|
ChargingMeterValuesUpdateV1 = "ChargingMeterValuesUpdateV1",
|
|
240
256
|
ChargingStoppedV1 = "ChargingStoppedV1",
|
|
@@ -418,6 +434,34 @@ export interface EnyoDataBusHeatpumpValuesV1 extends EnyoDataBusMessage {
|
|
|
418
434
|
gridOperatorLimit?: EnyoGridOperatorLimit;
|
|
419
435
|
};
|
|
420
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Live values of a heating rod (immersion / electric resistive heating
|
|
439
|
+
* element), published by the integration that owns the appliance.
|
|
440
|
+
*
|
|
441
|
+
* Send this whenever the power draw or the operating state changes, so an
|
|
442
|
+
* energy manager can account for the load and decide how much power to
|
|
443
|
+
* announce back via {@link EnyoDataBusSetHeatingRodAvailablePowerV2}.
|
|
444
|
+
*/
|
|
445
|
+
export interface EnyoDataBusHeatingRodValuesV1 extends EnyoDataBusMessage {
|
|
446
|
+
type: 'message';
|
|
447
|
+
message: EnyoDataBusMessageEnum.HeatingRodValuesUpdateV1;
|
|
448
|
+
/** ID of the appliance that delivered these values */
|
|
449
|
+
applianceId: string;
|
|
450
|
+
data: {
|
|
451
|
+
/**
|
|
452
|
+
* Current electrical power consumption of the heating rod in Watt.
|
|
453
|
+
* Always positive (a heating rod only consumes). Omit when the
|
|
454
|
+
* integration cannot measure it — do not send `0` as a stand-in for
|
|
455
|
+
* "unknown", since a consumer cannot tell that apart from "off".
|
|
456
|
+
*/
|
|
457
|
+
powerW?: number;
|
|
458
|
+
/**
|
|
459
|
+
* Current operating state of the heating element. Omit when the
|
|
460
|
+
* integration cannot determine it.
|
|
461
|
+
*/
|
|
462
|
+
state?: EnyoHeatingRodStateEnum;
|
|
463
|
+
};
|
|
464
|
+
}
|
|
421
465
|
export interface EnyoDataBusBatteryValuesUpdateV1 extends EnyoDataBusMessage {
|
|
422
466
|
type: 'message';
|
|
423
467
|
message: EnyoDataBusMessageEnum.BatteryValuesUpdateV1;
|
|
@@ -97,6 +97,22 @@ export var EnyoInverterStateEnum;
|
|
|
97
97
|
EnyoInverterStateEnum["Fault"] = "fault";
|
|
98
98
|
EnyoInverterStateEnum["Standby"] = "standby";
|
|
99
99
|
})(EnyoInverterStateEnum || (EnyoInverterStateEnum = {}));
|
|
100
|
+
/**
|
|
101
|
+
* Operating state of a heating rod as reported on the data bus via
|
|
102
|
+
* {@link EnyoDataBusHeatingRodValuesV1}.
|
|
103
|
+
*
|
|
104
|
+
* This is the *observed* state of the element, not the configured mode: the
|
|
105
|
+
* appliance metadata's {@link EnyoHeatingRodApplianceModeEnum} describes how the
|
|
106
|
+
* heating rod is set up, while this enum says whether it is drawing power right
|
|
107
|
+
* now.
|
|
108
|
+
*/
|
|
109
|
+
export var EnyoHeatingRodStateEnum;
|
|
110
|
+
(function (EnyoHeatingRodStateEnum) {
|
|
111
|
+
/** The heating element is not energized and consumes (close to) no power */
|
|
112
|
+
EnyoHeatingRodStateEnum["Idle"] = "idle";
|
|
113
|
+
/** The heating element is energized and heating */
|
|
114
|
+
EnyoHeatingRodStateEnum["Running"] = "running";
|
|
115
|
+
})(EnyoHeatingRodStateEnum || (EnyoHeatingRodStateEnum = {}));
|
|
100
116
|
export var EnyoStringStateEnum;
|
|
101
117
|
(function (EnyoStringStateEnum) {
|
|
102
118
|
EnyoStringStateEnum["Off"] = "off";
|
|
@@ -177,6 +193,7 @@ export var EnyoDataBusMessageEnum;
|
|
|
177
193
|
EnyoDataBusMessageEnum["ApplianceFlexibilityAnnouncementV1"] = "ApplianceFlexibilityAnnouncementV1";
|
|
178
194
|
EnyoDataBusMessageEnum["ApplianceStateUpdateV1"] = "ApplianceStateUpdateV1";
|
|
179
195
|
EnyoDataBusMessageEnum["HeatpumpValuesUpdateV1"] = "HeatpumpValuesUpdateV1";
|
|
196
|
+
EnyoDataBusMessageEnum["HeatingRodValuesUpdateV1"] = "HeatingRodValuesUpdateV1";
|
|
180
197
|
EnyoDataBusMessageEnum["ChargingStartedV1"] = "ChargingStartedV1";
|
|
181
198
|
EnyoDataBusMessageEnum["ChargingMeterValuesUpdateV1"] = "ChargingMeterValuesUpdateV1";
|
|
182
199
|
EnyoDataBusMessageEnum["ChargingStoppedV1"] = "ChargingStoppedV1";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { EnyoCurrencyEnum } from "./enyo-currency.js";
|
|
2
|
+
import { ForecastResolutionEnum } from "./enyo-forecasting.js";
|
|
3
|
+
/**
|
|
4
|
+
* A single cleared market price for one delivery period.
|
|
5
|
+
*
|
|
6
|
+
* Prices are the raw exchange result: they exclude grid fees, levies, taxes
|
|
7
|
+
* and any supplier margin, and they **can be negative** when generation
|
|
8
|
+
* exceeds demand.
|
|
9
|
+
*/
|
|
10
|
+
export interface EnyoEpexSpotPriceEntry {
|
|
11
|
+
/** Start of the delivery period in ISO format (inclusive) */
|
|
12
|
+
timestampIso: string;
|
|
13
|
+
/** End of the delivery period in ISO format (exclusive) */
|
|
14
|
+
endTimestampIso: string;
|
|
15
|
+
/**
|
|
16
|
+
* Cleared price per megawatt hour, in the series' currency. This is the
|
|
17
|
+
* unit the exchange publishes (e.g. `-14.2` for EUR -14.20/MWh).
|
|
18
|
+
*/
|
|
19
|
+
pricePerMwh: number;
|
|
20
|
+
/**
|
|
21
|
+
* The same price expressed per kilowatt hour (`pricePerMwh / 1000`), for
|
|
22
|
+
* consistency with the rest of the SDK, which prices energy per kWh.
|
|
23
|
+
*/
|
|
24
|
+
pricePerKwh: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A contiguous run of delivery periods whose spot price is below zero.
|
|
28
|
+
*
|
|
29
|
+
* Convenience shape for the common decision "should PV feed-in be curtailed /
|
|
30
|
+
* should a flexible load be pulled forward?" — see
|
|
31
|
+
* {@link EnyoInverterApplianceMetadata.blockFeedInOnNegativePrices}.
|
|
32
|
+
*/
|
|
33
|
+
export interface EnyoEpexNegativePriceWindow {
|
|
34
|
+
/** Start of the window in ISO format (inclusive) */
|
|
35
|
+
startIso: string;
|
|
36
|
+
/** End of the window in ISO format (exclusive) */
|
|
37
|
+
endIso: string;
|
|
38
|
+
/** Length of the window in minutes */
|
|
39
|
+
durationMinutes: number;
|
|
40
|
+
/** Most negative price per kWh observed inside the window */
|
|
41
|
+
minPricePerKwh: number;
|
|
42
|
+
/** Arithmetic mean price per kWh across the window's delivery periods */
|
|
43
|
+
averagePricePerKwh: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The EPEX SPOT day-ahead price series that applies to this device.
|
|
47
|
+
*/
|
|
48
|
+
export interface EnyoEpexSpotPrices {
|
|
49
|
+
/** Currency of every price in {@link entries} */
|
|
50
|
+
currency: EnyoCurrencyEnum;
|
|
51
|
+
/**
|
|
52
|
+
* Length of one delivery period. EPEX SPOT day-ahead moved to 15-minute
|
|
53
|
+
* periods in 2025, but older data is still hourly — read this instead of
|
|
54
|
+
* assuming a resolution.
|
|
55
|
+
*/
|
|
56
|
+
resolution: ForecastResolutionEnum;
|
|
57
|
+
/**
|
|
58
|
+
* When the host last fetched this series from the exchange, in ISO format.
|
|
59
|
+
* Useful to detect stale data when the device was offline.
|
|
60
|
+
*/
|
|
61
|
+
retrievedAtIso: string;
|
|
62
|
+
/** Price entries sorted ascending by `timestampIso`, without gaps */
|
|
63
|
+
entries: EnyoEpexSpotPriceEntry[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Restricts a price query to a time range.
|
|
67
|
+
*
|
|
68
|
+
* Both fields are optional: with an empty filter the host returns the full
|
|
69
|
+
* currently known window — typically today plus tomorrow, once the day-ahead
|
|
70
|
+
* auction has cleared.
|
|
71
|
+
*/
|
|
72
|
+
export interface EnyoEpexSpotPriceFilter {
|
|
73
|
+
/** Start of the requested range in ISO format (inclusive). Defaults to the start of the current delivery period. */
|
|
74
|
+
fromIso?: string;
|
|
75
|
+
/** End of the requested range in ISO format (exclusive). Defaults to the end of the known series. */
|
|
76
|
+
untilIso?: string;
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -27,4 +27,19 @@ export interface EnyoInverterApplianceMetadata {
|
|
|
27
27
|
yearBuilt?: number;
|
|
28
28
|
/** Groups of PV modules attached to this inverter, each with its own orientation */
|
|
29
29
|
moduleGroups?: EnyoInverterModuleGroup[];
|
|
30
|
+
/**
|
|
31
|
+
* Whether grid feed-in of this inverter should be blocked while the
|
|
32
|
+
* electricity price is negative.
|
|
33
|
+
*
|
|
34
|
+
* This is a user/installer configuration flag, not a live state: it
|
|
35
|
+
* expresses the intent that during negative market prices (see
|
|
36
|
+
* {@link EnergyAppEpexSpotPrice}) the inverter's export to the grid should
|
|
37
|
+
* be curtailed to 0 W, because feeding in costs money instead of earning
|
|
38
|
+
* it. Whoever controls the inverter — typically the energy manager sending
|
|
39
|
+
* `SetInverterFeedInLimitV1` — is responsible for honoring the flag and for
|
|
40
|
+
* lifting the curtailment once prices turn positive again.
|
|
41
|
+
*
|
|
42
|
+
* `undefined` means "not configured" and should be treated as `false`.
|
|
43
|
+
*/
|
|
44
|
+
blockFeedInOnNegativePrices?: boolean;
|
|
30
45
|
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED