From e6b329be67341e2186e8158cd614accadb076982 Mon Sep 17 00:00:00 2001 From: Igor Rutka Date: Tue, 4 Aug 2026 12:16:41 +0200 Subject: [PATCH 1/2] Added EMS documentation: setup guide, assets, GOPACS --- ems/README.md | 196 +----------- ems/docs/EmsSetup.md | 145 +++++++++ ems/docs/GopacsIntegration.md | 282 ++++++++++++++++++ ems/docs/assets/EmsDayAheadAsset.md | 22 ++ ems/docs/assets/EmsElectricityBatteryAsset.md | 36 +++ ems/docs/assets/EmsEnergyOptimisationAsset.md | 47 +++ 6 files changed, 543 insertions(+), 185 deletions(-) mode change 100644 => 100755 ems/README.md create mode 100755 ems/docs/EmsSetup.md create mode 100755 ems/docs/GopacsIntegration.md create mode 100755 ems/docs/assets/EmsDayAheadAsset.md create mode 100755 ems/docs/assets/EmsElectricityBatteryAsset.md create mode 100755 ems/docs/assets/EmsEnergyOptimisationAsset.md diff --git a/ems/README.md b/ems/README.md old mode 100644 new mode 100755 index de5a91c..51689d0 --- a/ems/README.md +++ b/ems/README.md @@ -1,191 +1,17 @@ -# Energy Management System (EMS) +# EMS documentation -## GOPACS Integration +**Setup** +- [EMS setup](docs/EmsSetup.md) -### What is GOPACS? -[GOPACS](https://www.gopacs.eu/) (Grid Operators Platform for Congestion Solutions) is a platform operated by Dutch grid operators (DSOs and TSO) to resolve grid congestion through flexibility trading. When the electricity grid is at risk of overloading, GOPACS sends flexibility requests to market participants (aggregators) who can adjust their energy consumption or production to relieve congestion. +**Assets** +- [Energy Optimisation Asset](docs/assets/EmsEnergyOptimisationAsset.md) + - [Electricity Battery Asset](docs/assets/EmsElectricityBatteryAsset.md) + - [Day Ahead Asset](docs/assets/EmsDayAheadAsset.md) -The communication between GOPACS and market participants uses the **UFTP** (Universal Flexibility Trading Protocol), part of the [USEF](https://www.usef.energy/) framework, implemented via the [Shapeshifter](https://github.com/shapeshifter/shapeshifter-library-java) library. -For detailed documentation, see: [GOPACS documents and manuals](https://www.gopacs.eu/en/documents-and-manuals/) +**Optimisation methods** +- [EMS optimisation](docs/optimisationMethods/EmsOptimisation.md) -### Getting Started - -To participate in GOPACS flex trading through OpenRemote, you need: - -1. **A GOPACS account** — Register as a Trading Company at [gopacs.eu](https://www.gopacs.eu/) -2. **OAuth2 client credentials** (`client_id` and `client_secret`) — See [OAuth2 Client Credentials for API Clients](https://www.gopacs.eu/wp-content/uploads/2025/12/GOPACS-OAuth2-Client-credentials-for-API-Clients-03-12-2025.pdf) -3. **A signing key pair** — An Ed25519 private key file for signing UFTP messages. The corresponding public key must be registered with GOPACS -4. **A contracted EAN** — The EAN (European Article Number) identifying your grid connection point, as agreed with your DSO - -#### Configuration - -The following environment variables must be set on the OpenRemote manager: - -| Variable | Required | Description | -| --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------- | -| `GOPACS_PRIVATE_KEY_FILE` | Yes | File path to the Ed25519 private key for signing UFTP messages | -| `GOPACS_CLIENT_ID` | Yes | OAuth2 client ID from GOPACS | -| `GOPACS_CLIENT_SECRET` | Yes | OAuth2 client secret from GOPACS | -| `GOPACS_PARTICIPANT_URL` | No | Address book base URL (default: `https://clc-message-broker.gopacs-services.eu`) | -| `GOPACS_OAUTH2_URL` | No | OAuth2 token endpoint (default: `https://auth.gopacs-services.eu/realms/gopacs/protocol/openid-connect/token`) | -| `GOPACS_RESPONSE_DELAY_SECONDS` | No | Delay before auto-responding to messages (default: `10`) | -| `GOPACS_FLEX_OFFER_DELAY_SECONDS` | No | Delay before sending a flex offer (default: `30`) | - -#### Asset Setup - -In OpenRemote, create an **EMS GOPACS Asset** as a child of an **EMS Energy Optimisation Asset** and set the `contractedEAN` attribute to your grid connection's EAN. - -Alternatively, when creating a new **EMS Energy Optimisation Asset**, you can enable the "Include GOPACS" attribute to have the GOPACS child asset created automatically. Note that this only works during initial asset creation — if the **EMS Energy Optimisation Asset** already exists, you need to manually create the **EMS GOPACS Asset** as a child. - -### Developer Guide - -#### Components - -``` -gopacs/ - GOPACSHandler.java Core orchestrator — handles all UFTP message processing, - signing, OAuth2 auth, and scheduling - GOPACSServerResource.java JAX-RS interface for the inbound endpoint (POST /gopacs/message) - GOPACSServerResourceImpl.java Delegates incoming XML to GOPACSHandler::processRawMessage - GOPACSAuthResource.java RESTEasy client proxy for OAuth2 token requests - GOPACSAddressBookResource.java RESTEasy client proxy for DSO participant lookup - FlexRequestISPTypeHelper.java Converts ISP numbers to timestamps (with DST handling) - OAuth2TokenResponse.java DTO for OAuth2 token responses -``` - -Related files outside this package: - -- `agent/EmsGOPACSAsset.java` — JPA entity defining the GOPACS asset type (contracted EAN, power attributes) -- `manager/EmsOptimisationService.java` — Manages `GOPACSHandler` lifecycle (creates/destroys handlers when assets are added/removed) -- `manager/EmsOptimisationSetupService.java` — Setup class that optionally creates GOPACS assets - -#### Data Flow - -OpenRemote acts as an **AGR (Aggregator)** in the UFTP protocol. The message exchange with the DSO (Distribution System Operator) follows this flow: - -```mermaid -sequenceDiagram - participant DSO as DSO (Grid Operator) - participant OR as OpenRemote (AGR) - - DSO->>OR: 1. FlexRequest - Note right of OR: DSO requests flexibility for a congestion point
(contains ISPs with max/min power limits) - - OR-->>DSO: 2. FlexRequestResponse - Note left of DSO: Auto-response after configurable delay - - OR-->>DSO: 3. FlexOffer - Note left of DSO: Sent after flex offer delay
(mirrors request, price EUR 0.00) - - DSO->>OR: 4. FlexOfferResponse - Note right of OR: DSO accepts or rejects the offer - - DSO->>OR: 5. FlexOrder - Note right of OR: DSO orders the accepted flexibility
(updates predicted data points on asset) - - OR-->>DSO: 6. FlexOrderResponse - Note left of DSO: Auto-response confirming the order -``` - -**How flex orders feed into optimisation:** - -1. `FlexOrder` power values are written as predicted data points on the `EmsGOPACSAsset` attributes (`powerLimitMaximumProfileFlexOrder`, `powerLimitMinimumProfileFlexOrder`) -2. `EmsOptimisationService.updatePowerLimitProfileTotalForecasts()` merges these GOPACS constraints with manual power limits from the parent `EmsEnergyOptimisationAsset` -3. The combined limits are used by the optimisation methods to constrain energy scheduling - -#### Inbound Endpoint - -The handler deploys a JAX-RS web application at `/gopacs`. Incoming signed UFTP XML messages are posted to: - -``` -POST /gopacs/message -Content-Type: application/xml -``` - -Processing steps: - -1. Deserialize signed XML envelope -2. Verify cryptographic signature using the sender's public key (from address book) -3. Deserialize UFTP payload -4. Process business logic (update asset attributes, schedule data points) -5. After a delay, send the auto-response (ensures the HTTP response is returned first) - -#### Authentication - -- **Inbound messages**: Verified using the DSO's public key, fetched from the GOPACS address book (`GET /v2/participants/DSO?contractedEan=`) and cached in memory -- **Outbound messages**: Signed with the private key from `GOPACS_PRIVATE_KEY_FILE`, delivered with an OAuth2 Bearer token obtained via client credentials flow from the GOPACS Keycloak instance - -#### ISP Handling - -ISPs (Imbalance Settlement Periods) are 15-minute intervals. `FlexRequestISPTypeHelper` converts ISP numbers to timestamps and includes special handling for European DST transitions (CET/CEST) on the last Sundays of March and October. - -## Redispatch (Intraday Congestion Management) - -### Overview - -In addition to the UFTP day-ahead flex trading described above, GOPACS provides a **Redispatch** mechanism for intraday congestion management. When a congestion situation is expected today, grid operators publish announcements requesting flexibility from market participants. - -The Redispatch flow is different from the UFTP flow: - -1. **Announcements** — GOPACS publishes congestion announcements via a REST API -2. **EAN effectivity** — CSPs check which of their EANs can help solve the congestion -3. **Bidding** — CSPs place buy/sell orders on connected trading platforms (ETPA, EPEX SPOT, NordPool) -4. **Matching** — The GOPACS algorithm matches orders across platforms -5. **Activation** — The trading platform notifies the CSP when an order is filled -6. **Delivery** — The CSP adjusts power as agreed - -### Configuration - -| Variable | Required | Description | -| ----------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `GOPACS_REDISPATCH_API_KEY` | Yes | API key from GOPACS UI (User Menu > Settings > Generate API-key); required to resolve EAN effectivity per announcement. Polling will not start without it. | -| `GOPACS_REDISPATCH_URL` | No | Base URL for the Redispatch API (default: `https://idcons.gopacs-services.eu`) | -| `GOPACS_REDISPATCH_POLL_INTERVAL_MINUTES` | No | Polling interval in minutes (default: `5`, minimum: `5`) | - -### Asset Setup - -On the **EMS GOPACS Asset**, configure: - -- **`redispatchEnabled`** — Set to `true` to start polling for announcements - -### Operator Workflow (Pilot Phase) - -1. When a relevant congestion announcement is detected, the asset attributes are updated with the announcement details -2. The `redispatchBidStatus` is set to `PENDING_CONFIRMATION` -3. The operator reviews the announcement info and suggested bid values -4. The operator sets `redispatchBidPrice` (EUR/MWh) and toggles `redispatchConfirmBid` to `true` -5. The bid is confirmed and logged (trading platform integration is pending) - -### Components - -``` -gopacs/ - GOPACSRedispatchHandler.java Polls announcements, checks EAN effectivity, manages bid workflow - GOPACSAnnouncementResource.java RESTEasy client proxy for /machineannouncements (public, no auth) - GOPACSEanEffectivityResource.java RESTEasy client proxy for EAN effectivity (API key auth) - dto/AnnouncementDto.java DTO for announcement JSON responses - dto/TimeSpanDto.java DTO for time span objects - dto/EanSolvingEffectivityDto.java DTO for EAN effectivity responses -``` - -### History - -Announcement and bid history are stored as time-series data points on `redispatchAnnouncementHistory` and `redispatchBidHistory` attributes, retained for 90 days. These are viewable in the OpenRemote history panel. - -### Future - -- **Trading platform integration** — When a platform is chosen (ETPA, EPEX SPOT, or NordPool), automated bid placement will be added -- **Automatic bidding** — After the pilot phase, the confirmation step will be optional -- **Bid pricing engine** — Dynamic bid pricing incorporating BRP imbalance costs, rebound costs, and opportunity costs - -### Testing - -GOPACS provides a dedicated testing environment. See [Testing UFTP API Flex Messages](https://www.gopacs.eu/wp-content/uploads/2025/12/GOPACS-Testing-receiving-and-sending-flex-messages-by-UFTP-testing-functionality-04-12-2025.pdf) for their guide on sending and receiving flex messages via the UFTP testing functionality. - -For additional context on the protocol and contract types, see [Flex Trading with CSC and ATR (UFTP Messages)](https://www.gopacs.eu/wp-content/uploads/2026/02/GOPACS-Flex-trading-with-Capacity-Limiting-Contracts-using-UFTP-messages-11-02-2026.pdf). - -#### Company Setup for Testing - -To configure your Trading Company for testing Capacity Steering Contracts, follow: [Company Settings for CSC Participation](https://www.gopacs.eu/wp-content/uploads/2025/06/GOPACS-Company-settings-for-participating-in-CSC-Capacity-Steering-Contracts.pdf) +**GOPACS** +- [GOPACS Integration](docs/Gopacs.md) diff --git a/ems/docs/EmsSetup.md b/ems/docs/EmsSetup.md new file mode 100755 index 0000000..bc31fc7 --- /dev/null +++ b/ems/docs/EmsSetup.md @@ -0,0 +1,145 @@ +# EMS setup + +This is a detailed instruction on how to set up an **Energy Management System (EMS)** using the EMS extension. + +⚠️ When encountering problems, always check the logs. + +### 1) Create EMS with manual power limit profiles + +1. Create asset: + * Go to the `Assets` page and click the `+` icon + * Select the `Ems Energy Optimisation Asset` and name it (**e.g. EMS**) + * Click `Add` to create the asset + +2. Generate power limit profiles: + * Set a value in the `Power limit maximum input (kW)` field (**e.g. 1000**) + * Click the `Generate power limit maximum profile manual input` + * Set a value in the `Power limit minimum input (kW)` field (**e.g. -1000**) + * Click the `Generate power limit minimum profile manual input` + +This will generate a power limit profile with 15-minute intervals for each day of the week in the `Power Limit Maximum Profile manual input` and `Power Limit Minimum Profile manual input` fields. Individual interval values can be adjusted manually. Once all modifications have been made, the updated profile can be saved by clicking the submit arrow. + +Alternatively, the values can be imported and modified in a spreadsheet application: + +- Copy the generated profile into a text editor, and save it as a `.csv` file +- Open the CSV file in a spreadsheet application, such as Google Sheets +- Make any required changes +- Export the updated profile as a `.csv` file (ensure that the values are `,` comma-separated) +- Open the exported CSV file in a text editor, and copy its contents into the respective `Power Limit Maximum Profile manual input` or `Power Limit Minimum Profile manual input` field. Click the submit arrow to save the changes + +**Note**: Configuration of either only a maximum profile, only a minimum profile, or both profiles is supported. The optimisation routine does not require both profiles to be defined. + +3. Display power limit profiles: + * Go to the `Insights` page and click the `+` icon + * Give the new dashboard a name (**e.g. Power limit profiles**) + * Create a `Line Chart` by dragging it onto the canvas + * Add the `Power limit maximum profile manual` and `Power limit minimum profile manual` attributes using the `+ Attribute` button in the Line Chart settings + * Set the **Time** `Default timeframe` to **Week** + * Click `Save`, then `View` to exit the `Modify` mode + +You now have a basic line chart showing your power limit profiles for this week. + +### 2) Add GOPACS + +**Note:** Only add a GOPACS asset if you want to use GOPACS in your EMS. + +1. Set up GOPACS: + * Set up GOPACS following the instructions in the [GOPACS Integration](docs/Gopacs.md) guide + +2. Create asset: + * Go to the `Assets` page, select the **EMS** asset created in section 1 and click the `+` icon + * Select the `Ems GOPACS Asset` and name it (**e.g. GOPACS**) + * Check if the parent is the **EMS** asset and click `Add` to create the asset + * Fill in the `Contracted EAN` field + +After completing the steps above, GOPACS flex requests can be received and are automatically validated and processed into flex orders. The flex orders are combined with the **manual power limit profiles** to create the `Power limit maximum profile total (kW)` and `Power limit minimum profile total (kW)` on the **EMS** asset. + +**Note:** GOPACS power limits can be used without setting manual power limits. + +### 3) Add battery + +1. Create asset: + * Go to the `Assets` page, select the **EMS** asset created in section 1 and click the `+` icon + * Select the `Ems electricity battery asset` and name it (**e.g. Battery 1**) + * Check if the parent is the **EMS** asset and click `Add` to create the asset + * Set values for the following attributes: + * `Charge efficiency (%)` (**e.g. 90**) + * `Discharge efficiency (%)` (**e.g. 90**) + * `Charge power maximum (kW)` (**e.g. 100**) + * `Discharge power maximum (kW)` (**e.g. -100**) + * `Energy capacity (kWh)` (**e.g. 400**) + * `Energy level percentage maximum (kW)` (**e.g. 90**) + * `Energy level percentage minimum (kW)` (**e.g. 10**) + * Connect the following attributes with your battery: + * `Energy level (kWh)` (Optional) + * `Energy level percentage (%)` + * `Power (kW)` + * Select the `Allow charging` and `Allow discharging` checkboxes to allow battery control by the EMS. + +Repeat the above steps to add multiple batteries to the EMS. + +**Note:** The `Ems Electricity Battery Asset` must be a child asset of the `Ems Energy Optimisation Asset` to be available as flexible power to the EMS. + +For more information about battery attributes, see the [Ems Electricity Battery Asset](assets/EmsElectricityBatteryAsset.md). + + +### 4) Connect EMS power attributes + +1. Connect attributes: + * Go to the `Assets` page, select the **EMS** asset created in section 1 + * Connect the `Power net (kW)` attribute with your main power meter + * Connect the `Power flexible (kW)` attribute with the sum of the `Power (kW)` attribute of the battery assets created in section 3 using a Flow rule or a custom Groovy rule + * (Optional) Connect the `Power production (kW)`attribute with the sum of the `Power (kW)` attribute of power producing assets, such as solar panels, using a Flow rule or a custom Groovy rule + * Calculate the `Power consumption (kW)` = `Power net (kW)` - `Power flexible (kW)` - `Power production (kW)` using a Flow rule or a custom Groovy rule + +For more information on how to use rules, see [Rules and Forecasting](https://docs.openremote.io/docs/category/rules-and-forecasting). + +### 5) Add power forecasts + +1. Add power consumption forecast: + * Go to the `Assets` page, select the **EMS** asset created in section 1 + * Click `Modify` and select the `powerConsumption` attribute + * Click `Add configuration items`, select `Forecast` and click `Add` + * Click the new Forecast field and select `Forecast Configuration Weighted Exponential Average` + * Set the following values: + * Forecast Count = 672 + * Forecast Period = PT15M + * Past Count = 3 + * Past Period = P7D + +Note: You can use a different forecast methods such as the `ML Forecasting Service`or your custom forecast method for the power consumption forecast. + +Step 2 is only required when there are power producing assets. + + +2. (Optional) Add power production forecast: + * Link the sum of the `Power (kW)` attribute forecasts of power producing assets, such as solar panels, using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. + +3. (Optional) Add power flexible forecast: + * Link the sum of the `Power (kW)` attribute forecasts of `Ems Electricity Battery Asset` assets using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. + +4. (Optional) Add power net forecast: + * Calculate the `Power Net (kW)` = `Power consumption (kW)` + `Power flexible (kW)` + `Power production (kW)` forecast using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. + + +### 6) Add tariffs forecasts +Add the [ENTSO-E extension](https://github.com/openremote/extensions/tree/main/entsoe/) to your project dependencies. For more information on how to include extensions, see [Extensions](https://docs.openremote.io/docs/developer-guide/extensions). + +1. Create asset: + * Go to the `Assets` page and click the `+` icon + * Select the `Entsoe agent` and name it (**e.g. ENTSO-E agent**) + * Click `Add` to create the asset + +2. Get EPEX spot prices: + * Follow the [ENTSO-E documentation](https://github.com/openremote/extensions/blob/main/entsoe/README.md) to connect the EPEX spot prices to a custom attribute (e.g. `nlPrices`) added to the `Entsoe agent` + +3. Link EPEX spot prices to EMS: + * Link the `nlPrices` attribute forecast of the **Entsoe agent** to the `tariffImport` of the `Ems Energy Optimisation Asset` using a custom Groovy rule. Don't forget to make the conversion from €/MWh to €/kWh. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. + * Repeat the above step for the `tariffExport` of the `Ems Energy Optimisation Asset`, and additionally multiply the forecast prices with -1. + +### 7) Select Optimisation method + * Go to the `Assets` page, select the **EMS** asset created in section 1 + * Find the `Optimisatiom method` attribute and select `Ems optimisation` from the dropdown menu. This will start the optimisation routine + +The EMS is now set up and the optimisation is running. For a detailed explanation about the `Ems optimisation` method, see (Work in progress) + diff --git a/ems/docs/GopacsIntegration.md b/ems/docs/GopacsIntegration.md new file mode 100755 index 0000000..dd1351f --- /dev/null +++ b/ems/docs/GopacsIntegration.md @@ -0,0 +1,282 @@ +## GOPACS Integration + +### What is GOPACS? + +[GOPACS](https://www.gopacs.eu/) (Grid Operators Platform for Congestion Solutions) is a platform operated by Dutch grid +operators (DSOs and TSO) to manage electricity grid congestion through market-based electricity flexibility trading. +When the grid is at risk of overloading, GOPACS publishes flexibility requests that can be fulfilled by market +participants, such as Congestion Service Providers (CSPs) and aggregators, who can adjust electricity consumption or +production to help relieve congestion. + +Communication between GOPACS and market participants is based on the UFTP (USEF Flexibility Trading Protocol), which +originated from the [USEF](https://www.usef.energy/) (Universal Smart Energy Framework). The protocol is implemented +through the [Shapeshifter](https://github.com/shapeshifter/shapeshifter-library-java) library. + +For detailed information, see the [GOPACS documents and manuals](https://www.gopacs.eu/en/documents-and-manuals/). + +### Getting Started + +To participate in GOPACS flex trading through OpenRemote, you need the following:: + +1. **GOPACS account** — Register as a Trading Company at [gopacs.eu](https://www.gopacs.eu/) +2. **OAuth2 client credentials** (`client_id` and `client_secret`) — + See [OAuth2 Client Credentials for API Clients](https://www.gopacs.eu/wp-content/uploads/2025/12/GOPACS-OAuth2-Client-credentials-for-API-Clients-03-12-2025.pdf) +3. **Signing key pair** — An Ed25519 private key file for signing UFTP messages. The corresponding public key must be + registered with GOPACS +4. **Contracted EAN** — The EAN (European Article Number) identifying your grid connection point, as agreed with your + DSO + +#### Configuration + +The following environment variables must be set on the OpenRemote manager: + +| Variable | Required | Description | +|-----------------------------------|----------|----------------------------------------------------------------------------------------------------------------| +| `GOPACS_PRIVATE_KEY_FILE` | Yes | File path to the Ed25519 private key for signing UFTP messages | +| `GOPACS_CLIENT_ID` | Yes | OAuth2 client ID from GOPACS | +| `GOPACS_CLIENT_SECRET` | Yes | OAuth2 client secret from GOPACS | +| `GOPACS_PARTICIPANT_URL` | No | Address book base URL (default: `https://clc-message-broker.gopacs-services.eu`) | +| `GOPACS_OAUTH2_URL` | No | OAuth2 token endpoint (default: `https://auth.gopacs-services.eu/realms/gopacs/protocol/openid-connect/token`) | +| `GOPACS_RESPONSE_DELAY_SECONDS` | No | Delay before auto-responding to messages (default: `10`) | +| `GOPACS_FLEX_OFFER_DELAY_SECONDS` | No | Delay before sending a flex offer (default: `30`) | + +#### Asset Setup + +In OpenRemote, create an `Ems GOPACS Asset` as a child of an `Ems Energy Optimisation Asset` and set the `contractedEAN` +attribute to your grid connection's EAN. + +### Developer Guide + +#### Components + +``` +gopacs/ + GOPACSHandler.java Core orchestrator — handles all UFTP message processing, + signing, OAuth2 auth, and scheduling + GOPACSServerResource.java JAX-RS interface for the inbound endpoint (POST /gopacs/message) + GOPACSServerResourceImpl.java Delegates incoming XML to GOPACSHandler::processRawMessage + GOPACSAuthResource.java RESTEasy client proxy for OAuth2 token requests + GOPACSAddressBookResource.java RESTEasy client proxy for DSO participant lookup + FlexRequestISPTypeHelper.java Converts ISP numbers to timestamps (with DST handling) + OAuth2TokenResponse.java DTO for OAuth2 token responses +``` + +#### Data Flow + +OpenRemote acts as an **AGR (Aggregator)** in the UFTP protocol. The message exchange with the DSO (Distribution System +Operator) follows this flow: + +```mermaid +sequenceDiagram + participant DSO as DSO (Grid Operator) + participant OR as OpenRemote (AGR) + + DSO->>OR: 1. FlexRequest + Note right of OR: DSO requests flexibility for a congestion point
(contains ISPs with max/min power limits) + + OR-->>DSO: 2. FlexRequestResponse + Note left of DSO: Auto-response after configurable delay + + OR-->>DSO: 3. FlexOffer + Note left of DSO: Sent after flex offer delay
(mirrors request, price EUR 0.00) + + DSO->>OR: 4. FlexOfferResponse + Note right of OR: DSO accepts or rejects the offer + + DSO->>OR: 5. FlexOrder + Note right of OR: DSO orders the accepted flexibility
(updates predicted data points on asset) + + OR-->>DSO: 6. FlexOrderResponse + Note left of DSO: Auto-response confirming the order +``` + +**How flex orders feed into optimisation:** + +1. Flex order power values are written as predicted data points on the `Ems GOPACS Asset` attributes ( + `powerLimitMaximumProfileFlexOrder`, `powerLimitMinimumProfileFlexOrder`) +2. `EmsOptimisationService.updatePowerLimitProfileTotalForecasts()` merges these GOPACS constraints with manual power + limits from the parent `Ems energy optimisation asset` +3. The combined limits are used by the optimisation methods to constrain energy scheduling + +#### Inbound Endpoint + +The handler deploys a JAX-RS web application at `/gopacs`. Incoming signed UFTP XML messages are posted to: + +``` +POST /gopacs/message +Content-Type: application/xml +``` + +Processing steps: + +1. Deserialize signed XML envelope +2. Verify cryptographic signature using the sender's public key (from address book) +3. Deserialize UFTP payload +4. Process business logic (update asset attributes, schedule data points) +5. After a delay, send the auto-response (ensures the HTTP response is returned first) + +#### Authentication + +- **Inbound messages**: Verified using the DSO's public key, fetched from the GOPACS address book ( + `GET /v2/participants/DSO?contractedEan=`) and cached in memory +- **Outbound messages**: Signed with the private key from `GOPACS_PRIVATE_KEY_FILE`, delivered with an OAuth2 Bearer + token obtained via client credentials flow from the GOPACS Keycloak instance + +#### ISP Handling + +ISPs (Imbalance Settlement Periods) are 15-minute intervals. `FlexRequestISPTypeHelper` converts ISP numbers to +timestamps and includes special handling for European Daylight Saving Time transitions (CET/CEST) on the last Sundays of +March and October. + +## Redispatch (Intraday Congestion Management) + +### Overview + +In addition to the UFTP day-ahead flex trading described above, GOPACS provides a **Redispatch** mechanism for intraday +congestion management. When a congestion situation is expected during the day, grid operators publish announcements +requesting flexibility from market participants. + +> Prerequisites are the same as UFTP — see [Getting Started](#getting-started) for the GOPACS account and contracted +> EAN. Redispatch additionally requires an API key (see [Configuration](#configuration-1) below). + +The Redispatch flow is different from the UFTP flow: + +1. **Announcements** — GOPACS publishes congestion announcements via a REST API +2. **EAN effectivity** — CSPs check which of their EANs can help solve the congestion +3. **Bidding** — CSPs place buy/sell orders on connected trading platforms (ETPA, EPEX SPOT, NordPool) +4. **Matching** — The GOPACS algorithm matches orders across platforms +5. **Activation** — The trading platform notifies the CSP when an order is filled +6. **Delivery** — The CSP adjusts power as agreed + +```mermaid +sequenceDiagram + participant Op as Operator + participant OR as OpenRemote (CSP) + participant API as GOPACS Redispatch API + participant TP as Trading Platform (future) + + loop every poll interval (≥ 5 min) + OR->>API: GET /machineannouncements (CONGESTIONMANAGEMENT, ANNOUNCEMENT_OPEN) + API-->>OR: announcements + Note right of OR: Record every newly-seen announcement in history + OR->>API: GET .../eansolvingeffectivity per announcement + API-->>OR: EAN categories per announcement + Note right of OR: Keep announcements where the contracted EAN
is listed; prefer MANDATORY over VOLUNTARY + OR->>OR: On a new selection: update redispatch* attributes,
record a second history entry with effectivity,
set redispatchBidStatus = PENDING_CONFIRMATION + end + + Op->>OR: Set redispatchBidPrice, toggle redispatchConfirmBid = true + OR->>OR: Log bid, set redispatchBidStatus = CONFIRMED + OR-->>TP: Place order (not yet implemented) +``` + +**Selection rules:** per poll, the handler keeps only `CONGESTIONMANAGEMENT` / `ANNOUNCEMENT_OPEN` announcements where +the contracted EAN appears in some EAN-effectivity category, then prefers `MANDATORY` over `VOLUNTARY` compliance type +when more than one matches. + +### Configuration + +| Variable | Required | Description | +|-------------------------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `GOPACS_REDISPATCH_API_KEY` | Yes | API key from GOPACS UI (User Menu > Settings > Generate API-key); required to resolve EAN effectivity per announcement. Polling will not start without it. | +| `GOPACS_REDISPATCH_URL` | No | Base URL for the Redispatch API (default: `https://idcons.gopacs-services.eu`) | +| `GOPACS_REDISPATCH_POLL_INTERVAL_MINUTES` | No | Polling interval in minutes (default: `5`, minimum: `5`) | + +### Asset attributes + +On the `EMS GOPACS Asset` set the **`redispatchEnabled`** to `true` to start polling for announcements. The user can set the `redispatchBidPrice` and `redispatchConfirmBid`. All status, bid-suggestion and history attributes +are read-only. + +| Group | Attribute | Value Type | Units | Read-only | Purpose | +|---------------|---------------------------------|-------------|---------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Configuration | `redispatchEnabled` | Boolean | - | | Turn redispatch on/off | +| Announcement | `redispatchAnnouncementId` | Text | - | ✓ | ID of the currently selected announcement, if any. | +| Announcement | `redispatchComplianceType` | Text | - | ✓ | `MANDATORY` or `VOLUNTARY`. | +| Announcement | `redispatchAnnouncementMessage` | Text | - | ✓ | Free-text description from the DSO. | +| Announcement | `redispatchStartTime` | Timestamp | - | ✓ | Start of the problem period. | +| Announcement | `redispatchEndTime` | Timestamp | - | ✓ | End of the problem period. | +| Announcement | `redispatchBidValidityEnd` | Timestamp | - | ✓ | Latest moment a bid can still be submitted for this announcement. | +| Announcement | `redispatchRequestedPower` | Number | kW | ✓ | Remaining problem profile, written as predicted data points (15-min ISP grid, 7-day retention). | +| Announcement | `redispatchEanEffectivity` | Text | - | ✓ | Effectivity category in which the contracted EAN was matched (e.g. `THREE_PHASE_NETWORK_REDUCE`). | +| Announcement | `redispatchRequestAreaBuy` | Text | - | ✓ | DSO-supplied area description for buy orders. | +| Announcement | `redispatchRequestAreaSell` | Text | - | ✓ | DSO-supplied area description for sell orders. | +| Announcement | `redispatchLastPoll` | timestamp | - | ✓ | Timestamp of the last completed poll cycle (only updated when the API responded). | +| Bid | `redispatchBidPrice` | Number | EUR/MWh | | Operator-supplied bid price. | +| Bid | `redispatchSuggestedPower` | Number | kW | ✓ | _Not yet populated — pending bid pricing strategy follow-up._ | +| Bid | `redispatchSuggestedVolume` | Number | kWh | ✓ | _Not yet populated — pending bid pricing strategy follow-up._ | +| Workflow | `redispatchConfirmBid` | Boolean | - | | Set to `true` to confirm the active bid. Handler resets it after processing. | +| Workflow | `redispatchBidStatus` | Text | - | ✓ | Bid status:
- `NONE` no active announcement
- `PENDING_CONFIRMATION` operator action required
- `CONFIRMED` bid logged (and, in future, sent to the trading platform) | +| History | `redispatchAnnouncementHistory` | JSON object | - | ✓ | Last polled announcement, see attribute history for announcement history. | +| History | `redispatchBidHistory` | JSON object | - | ✓ | Last confirmed bid, see attribute history for confirmed bid history. | + +### Operator Workflow (Pilot Phase) + +1. When a relevant congestion announcement is detected, the asset attributes are updated with the announcement details +2. The `redispatchBidStatus` is set to `PENDING_CONFIRMATION` +3. The operator reviews the announcement info and suggested bid values +4. The operator sets `redispatchBidPrice` (EUR/MWh) and toggles `redispatchConfirmBid` to `true` +5. The bid is confirmed and logged (trading platform integration is pending) + +### Resilience and polling + +- The polling interval is clamped to a minimum of 5 minutes because GOPACS recommends spacing requests at least that far + apart. +- HTTP errors and exceptions on the announcements endpoint **skip the poll and preserve current attributes**, so + transient API hiccups do not flap the bid status. Any *successful* poll (HTTP 200) that yields no announcement + selected for the contracted EAN clears the active announcement and resets `redispatchBidStatus` to `NONE`. That covers + three cases: the response is empty, the response has announcements but none are open `CONGESTIONMANAGEMENT`, or some + are but the contracted EAN is not listed in their EAN-effectivity categories. Only a failed fetch (HTTP error / + exception) leaves the previous announcement untouched. +- A *persistent* non-200 (e.g. an invalid API key returning 401, or a sustained outage) keeps the previously selected + announcement on screen indefinitely. If `redispatchLastPoll` falls behind the configured interval, check the manager + logs for `Failed to fetch announcements: HTTP …` (warning) or `Error fetching announcements` (severe). +- The handler **refuses to start** (logs `SEVERE`) when `GOPACS_REDISPATCH_API_KEY` is unset — without it there is no + way to resolve EAN effectivity per announcement. +- Toggling `redispatchEnabled` off then on restarts the handler. The same applies when `contractedEAN` is changed. + Useful when you need to force a clean state. + +### Components + +``` +gopacs/ + GOPACSRedispatchHandler.java Polls announcements, checks EAN effectivity, manages bid workflow + GOPACSAnnouncementResource.java RESTEasy client proxy for /machineannouncements (public, no auth) + GOPACSEanEffectivityResource.java RESTEasy client proxy for EAN effectivity (API key auth) + dto/AnnouncementDto.java DTO for announcement JSON responses + dto/TimeSpanDto.java DTO for time span objects + dto/EanSolvingEffectivityDto.java DTO for EAN effectivity responses +``` + +### History + +Announcement and bid history are stored as time-series data points on `redispatchAnnouncementHistory` and +`redispatchBidHistory`, retained for 90 days and viewable in the OpenRemote history panel. + +`redispatchAnnouncementHistory` records **every** polled announcement on first sight (including ones that the +EAN-effectivity check later rejects), so the audit trail captures everything GOPACS returned during the handler's +lifetime — not just the announcements that became active. When an announcement is then *selected* on a poll, a second, +richer history entry is recorded with the matched effectivity details, so an active announcement will appear twice in +the timeline (once at first sight, once on selection). To keep memory bounded for long-running handlers, the running set +of already-recorded announcement IDs is capped at 10 000 entries (oldest inserted IDs are evicted first — +insertion-order/FIFO). + +### Future + +- **Trading platform integration** — When a platform is chosen (ETPA, EPEX SPOT, or NordPool), automated bid placement + will be added +- **Automatic bidding** — After the pilot phase, the confirmation step will be optional +- **Bid pricing engine** — Dynamic bid pricing incorporating BRP imbalance costs, rebound costs, and opportunity costs + +### Testing + +GOPACS provides a dedicated testing environment. +See [Testing UFTP API Flex Messages](https://www.gopacs.eu/wp-content/uploads/2025/12/GOPACS-Testing-receiving-and-sending-flex-messages-by-UFTP-testing-functionality-04-12-2025.pdf) +for their guide on sending and receiving flex messages via the UFTP testing functionality. + +For additional context on the protocol and contract types, +see [Flex Trading with CSC and ATR (UFTP Messages)](https://www.gopacs.eu/wp-content/uploads/2026/02/GOPACS-Flex-trading-with-Capacity-Limiting-Contracts-using-UFTP-messages-11-02-2026.pdf). + +#### Company Setup for Testing + +To configure your Trading Company for testing Capacity Steering Contracts, +follow: [Company Settings for CSC Participation](https://www.gopacs.eu/wp-content/uploads/2025/06/GOPACS-Company-settings-for-participating-in-CSC-Capacity-Steering-Contracts.pdf) \ No newline at end of file diff --git a/ems/docs/assets/EmsDayAheadAsset.md b/ems/docs/assets/EmsDayAheadAsset.md new file mode 100755 index 0000000..c4a06b3 --- /dev/null +++ b/ems/docs/assets/EmsDayAheadAsset.md @@ -0,0 +1,22 @@ +# Ems Day Ahead Asset + +For detailed setup instructions, see the [EMS Setup](../EmsSetup.md) guide. + +The `Ems Day Ahead Asset` must be a child asset of the `Ems Energy Optimisation Asset` to be available to the EMS. This asset is used to store day-ahead tariff forecasts at a user-specified time of day. + +## Input attributes + +### Set attributes: + +| Attribute Name | Value Type | Units | Description | +|------------------------------|------------|-------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `collectTimeForecasts` | Text | - | Sets the time of day at which the forecast is collected from the `Ems Energy Optimisation Asset` for the `tariffExport` and `tariffImport` attributes. Example: 10:00 | +| `useTariffDayAheadForecasts` | Boolean | - | When enabled, `tariffExportDayAheadForecast` and `tariffImportDayAheadForecast` are used as the day-ahead tariffs for optimisation instead of the `tariffExport` and `tariffImport` attributes from the `Ems Energy Optimisation Asset`. | + +## Output attributes + +| Attribute Name | Value Type | Units | Description | +|--------------------------------|------------|-------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `lastUpdateForecasts` | Text | - | Displays when the forecasts for the `Ems Day Ahead Asset` were last updated | +| `tariffExportDayAheadForecast` | Number | €/kWh | Stores the `tariffExport` attribute from the `Ems Energy Optimisation Asset` for the day-ahead at the time set by `collectTimeForecasts`. (Tariffs are stored directly in the database. The attribute value itself remains empty to prevent duplicate data points from being stored.) | +| `tariffImportDayAheadForecast` | Number | €/kWh | Stores the `tariffImport` attribute from the `Ems Energy Optimisation Asset` for the day-ahead at the time set by `collectTimeForecasts`. (Tariffs are stored directly in the database. The attribute value itself remains empty to prevent duplicate data points from being stored.) | diff --git a/ems/docs/assets/EmsElectricityBatteryAsset.md b/ems/docs/assets/EmsElectricityBatteryAsset.md new file mode 100755 index 0000000..dd95f87 --- /dev/null +++ b/ems/docs/assets/EmsElectricityBatteryAsset.md @@ -0,0 +1,36 @@ +# Ems Electricity Battery Asset + +For detailed setup instructions, see the [EMS Setup](../EmsSetup.md) guide. + +The `Ems Electricity Battery Asset` must be a child asset of the `Ems Energy Optimisation Asset` to be available as flexible power to the EMS. + +## Input attributes + +### Set attributes: + +| Attribute Name | Value Type | Units | Description | +|--------------------------------|------------------|-------|------------------------------------------------------| +| `allowCharging` | Boolean | - | Allow the EMS to control charging of the battery. | +| `allowDischarging` | Boolean | - | Allow the EMS to control discharging of the battery. | +| `chargeEfficiency` | Positive integer | % | Efficiency of the charging process. | +| `chargePowerMaximum` | Positive number | kW | Maximum allowed charging power. | +| `dischargeEfficiency` | Positive integer | % | Efficiency of the discharging process. | +| `dischargePowerMaximum` | Negative number | kW | Maximum allowed discharging power (negative). | +| `energyCapacity` | Positive number | kWh | Total energy capacity of the battery. | +| `energyLevelPercentageMaximum` | Positive integer | % | Maximum allowed energy level percentage. | +| `energyLevelPercentageMinimum` | Positive integer | % | Minimum allowed energy level percentage. | + +### Connect attributes: + +| Attribute Name | Value Type | Units | Description | +|-------------------------|-----------------|-------|-------------------------------------------------------------------| +| `energyLevel` | Positive number | kWh | Current stored energy in the battery (Optional). | +| `energyLevelPercentage` | Positive number | % | Current energy level percentage. | +| `power` | Number | kW | Current power flow (positive = charging, negative = discharging). | + +## Output attributes + +| Attribute Name | Value Type | Units | Description | +|--------------------|------------|-------|--------------------------------------------------------------| +| `connectionStatus` | Enum | - | Current connection status of the battery. | +| `powerSetpoint` | Number | kW | Current active battery power setpoint calculated by the EMS. | diff --git a/ems/docs/assets/EmsEnergyOptimisationAsset.md b/ems/docs/assets/EmsEnergyOptimisationAsset.md new file mode 100755 index 0000000..0b13305 --- /dev/null +++ b/ems/docs/assets/EmsEnergyOptimisationAsset.md @@ -0,0 +1,47 @@ +# Ems Energy Optimisation Asset + +For detailed setup instructions, see the [EMS Setup](../EmsSetup.md) guide. + +The `Ems Energy Optimisation Asset` serves as the parent asset of the Energy Management System (EMS). Other assets that are part of the EMS extension, such as the `Ems Electricity Battery Asset`, must be added as child assets before they can be used by the EMS. + +## Input attributes + +### Set attributes: + +| Attribute Name | Value Type | Units | Description | +|-----------------------------------------------|------------|-------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `enableDetailedLogging` | Boolean | - | Enables detailed logging (optimisation method specific). | +| `generatePowerLimitMaximumProfileManualInput` | Boolean | - | Generates maximum power limit profile based on the `powerLimitMaximumInput` value. | +| `generatePowerLimitMinimumProfileManualInput` | Boolean | - | Generates minimum power limit profile based on the `powerLimitMinimumInput` value. | +| `optimisationDisabled` | Boolean | - | Disables optimisation logic, including power limit profiles. | +| `optimisationMethod` | Enum | - | Selects the optimisation strategy. | +| `powerLimitMaximumInput` | Number | kW | Power limit maximum, used to generate `powerLimitMaximumProfileManualInput`. | +| `powerLimitMaximumProfileManualInput` | Text | - | Multiline input for manual maximum power limit profile. First, generate the general profile using `generatePowerLimitMaximumProfileManualInput`. Then, adjust the power limits for individual 15-minute intervals. | +| `powerLimitMinimumInput` | Number | kW | Power limit minimum, used to generate `powerLimitMinimumProfileManualInput`. | +| `powerLimitMinimumProfileManualInput` | Text | - | Multiline input for manual minimum power limit profile. First, generate the general profile using `generatePowerLimitMinimumProfileManualInput`. Then, adjust the power limits for individual 15-minute intervals. | + +### Connect attributes: + +The exact way in which the following attributes are connected is left to the user. This is intentional and allows maximum flexibility when configuring the EMS based on the available power meter readings. The last column provides an example of how each attribute can be connected. Attribute values can be calculated using either _Flow rules_ or, for more advanced use cases, _Groovy rules_. Ensure that the `Rule state` configuration item is added to all attributes involved in the calculations. + + +| Attribute Name | Value Type | Units | Description | Connection example | +|---------------------|------------|-------|---------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `energyExportTotal` | Number | kWh | Total energy exported | Connect: main meter. (Not required for EMS optimisation) | +| `energyImportTotal` | Number | kWh | Total energy imported | Connect: main meter. (Not required for EMS optimisation) | +| `powerConsumption` | Number | kW | Current power consumption. | Calculate: sum of power consumer assets or, powerConsumption = powerNet - powerFlexible - powerProduction. | +| `powerFlexible` | Number | kW | Current power from flexible assets, such as batteries, charges etc. | Calculate: sum of power flexible assets. | +| `powerNet` | Number | kW | Current net power. | Connect: main meter. | +| `powerProduction` | Number | kW | Current power from power producing assets, such as solar panels. | Calculate: sum of power production assets. | +| `tariffExport` | Number | €/kWh | Export tariff used by the EMS for optimisation | Connect: ENTSO-E agent for EPEX spot prices in your region. | +| `tariffImport` | Number | €/kWh | Import tariff used by the EMS for optimisation | Connect: ENTSO-E agent for EPEX spot prices in your region. | + +## Output attributes + +| Attribute Name | Value Type | Units | Description | +|----------------------------------|------------|-------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `advancedSettingsAttributes` | Text | - | List of advanced settings attributes that can be added manually for the selected `optimisationMethod` (Only visible in `MODIFY` mode, optimisation method specific). | +| `powerLimitMaximumProfileManual` | Number | kW | Current manual maximum power limit based on `powerLimitMaximumProfileManualInput`. | +| `powerLimitMaximumProfileTotal` | Number | kW | Current total maximum power limit, calculated as the sum of the manual power limit and the GOPACS order power limit. This value is used for optimisation. | +| `powerLimitMinimumProfileManual` | Number | kW | Current manual minimum power limit based on `powerLimitMinimumProfileManualInput`. | +| `powerLimitMinimumProfileTotal` | Number | kW | Current total minimum power limit, calculated as the sum of the manual power limit and the GOPACS order power limit. This value is used for optimisation. | From e85119cc52d166a0605f489a82cf651e53cea1e4 Mon Sep 17 00:00:00 2001 From: Igor Rutka Date: Tue, 4 Aug 2026 13:08:33 +0200 Subject: [PATCH 2/2] Applied spotless formatting --- ems/README.md | 6 +- ems/docs/EmsSetup.md | 126 +++++++++--------- ems/docs/GopacsIntegration.md | 14 +- ems/docs/assets/EmsDayAheadAsset.md | 4 +- ems/docs/assets/EmsElectricityBatteryAsset.md | 8 +- ems/docs/assets/EmsEnergyOptimisationAsset.md | 7 +- 6 files changed, 82 insertions(+), 83 deletions(-) diff --git a/ems/README.md b/ems/README.md index 51689d0..89c59a1 100755 --- a/ems/README.md +++ b/ems/README.md @@ -1,17 +1,19 @@ # EMS documentation **Setup** -- [EMS setup](docs/EmsSetup.md) +- [EMS setup](docs/EmsSetup.md) **Assets** + - [Energy Optimisation Asset](docs/assets/EmsEnergyOptimisationAsset.md) - [Electricity Battery Asset](docs/assets/EmsElectricityBatteryAsset.md) - [Day Ahead Asset](docs/assets/EmsDayAheadAsset.md) - **Optimisation methods** + - [EMS optimisation](docs/optimisationMethods/EmsOptimisation.md) **GOPACS** + - [GOPACS Integration](docs/Gopacs.md) diff --git a/ems/docs/EmsSetup.md b/ems/docs/EmsSetup.md index bc31fc7..8a48db5 100755 --- a/ems/docs/EmsSetup.md +++ b/ems/docs/EmsSetup.md @@ -7,15 +7,15 @@ This is a detailed instruction on how to set up an **Energy Management System (E ### 1) Create EMS with manual power limit profiles 1. Create asset: - * Go to the `Assets` page and click the `+` icon - * Select the `Ems Energy Optimisation Asset` and name it (**e.g. EMS**) - * Click `Add` to create the asset + - Go to the `Assets` page and click the `+` icon + - Select the `Ems Energy Optimisation Asset` and name it (**e.g. EMS**) + - Click `Add` to create the asset 2. Generate power limit profiles: - * Set a value in the `Power limit maximum input (kW)` field (**e.g. 1000**) - * Click the `Generate power limit maximum profile manual input` - * Set a value in the `Power limit minimum input (kW)` field (**e.g. -1000**) - * Click the `Generate power limit minimum profile manual input` + - Set a value in the `Power limit maximum input (kW)` field (**e.g. 1000**) + - Click the `Generate power limit maximum profile manual input` + - Set a value in the `Power limit minimum input (kW)` field (**e.g. -1000**) + - Click the `Generate power limit minimum profile manual input` This will generate a power limit profile with 15-minute intervals for each day of the week in the `Power Limit Maximum Profile manual input` and `Power Limit Minimum Profile manual input` fields. Individual interval values can be adjusted manually. Once all modifications have been made, the updated profile can be saved by clicking the submit arrow. @@ -24,18 +24,18 @@ Alternatively, the values can be imported and modified in a spreadsheet applicat - Copy the generated profile into a text editor, and save it as a `.csv` file - Open the CSV file in a spreadsheet application, such as Google Sheets - Make any required changes -- Export the updated profile as a `.csv` file (ensure that the values are `,` comma-separated) +- Export the updated profile as a `.csv` file (ensure that the values are `,` comma-separated) - Open the exported CSV file in a text editor, and copy its contents into the respective `Power Limit Maximum Profile manual input` or `Power Limit Minimum Profile manual input` field. Click the submit arrow to save the changes **Note**: Configuration of either only a maximum profile, only a minimum profile, or both profiles is supported. The optimisation routine does not require both profiles to be defined. 3. Display power limit profiles: - * Go to the `Insights` page and click the `+` icon - * Give the new dashboard a name (**e.g. Power limit profiles**) - * Create a `Line Chart` by dragging it onto the canvas - * Add the `Power limit maximum profile manual` and `Power limit minimum profile manual` attributes using the `+ Attribute` button in the Line Chart settings - * Set the **Time** `Default timeframe` to **Week** - * Click `Save`, then `View` to exit the `Modify` mode + - Go to the `Insights` page and click the `+` icon + - Give the new dashboard a name (**e.g. Power limit profiles**) + - Create a `Line Chart` by dragging it onto the canvas + - Add the `Power limit maximum profile manual` and `Power limit minimum profile manual` attributes using the `+ Attribute` button in the Line Chart settings + - Set the **Time** `Default timeframe` to **Week** + - Click `Save`, then `View` to exit the `Modify` mode You now have a basic line chart showing your power limit profiles for this week. @@ -44,13 +44,13 @@ You now have a basic line chart showing your power limit profiles for this week. **Note:** Only add a GOPACS asset if you want to use GOPACS in your EMS. 1. Set up GOPACS: - * Set up GOPACS following the instructions in the [GOPACS Integration](docs/Gopacs.md) guide + - Set up GOPACS following the instructions in the [GOPACS Integration](docs/Gopacs.md) guide 2. Create asset: - * Go to the `Assets` page, select the **EMS** asset created in section 1 and click the `+` icon - * Select the `Ems GOPACS Asset` and name it (**e.g. GOPACS**) - * Check if the parent is the **EMS** asset and click `Add` to create the asset - * Fill in the `Contracted EAN` field + - Go to the `Assets` page, select the **EMS** asset created in section 1 and click the `+` icon + - Select the `Ems GOPACS Asset` and name it (**e.g. GOPACS**) + - Check if the parent is the **EMS** asset and click `Add` to create the asset + - Fill in the `Contracted EAN` field After completing the steps above, GOPACS flex requests can be received and are automatically validated and processed into flex orders. The flex orders are combined with the **manual power limit profiles** to create the `Power limit maximum profile total (kW)` and `Power limit minimum profile total (kW)` on the **EMS** asset. @@ -59,22 +59,22 @@ After completing the steps above, GOPACS flex requests can be received and are a ### 3) Add battery 1. Create asset: - * Go to the `Assets` page, select the **EMS** asset created in section 1 and click the `+` icon - * Select the `Ems electricity battery asset` and name it (**e.g. Battery 1**) - * Check if the parent is the **EMS** asset and click `Add` to create the asset - * Set values for the following attributes: - * `Charge efficiency (%)` (**e.g. 90**) - * `Discharge efficiency (%)` (**e.g. 90**) - * `Charge power maximum (kW)` (**e.g. 100**) - * `Discharge power maximum (kW)` (**e.g. -100**) - * `Energy capacity (kWh)` (**e.g. 400**) - * `Energy level percentage maximum (kW)` (**e.g. 90**) - * `Energy level percentage minimum (kW)` (**e.g. 10**) - * Connect the following attributes with your battery: - * `Energy level (kWh)` (Optional) - * `Energy level percentage (%)` - * `Power (kW)` - * Select the `Allow charging` and `Allow discharging` checkboxes to allow battery control by the EMS. + - Go to the `Assets` page, select the **EMS** asset created in section 1 and click the `+` icon + - Select the `Ems electricity battery asset` and name it (**e.g. Battery 1**) + - Check if the parent is the **EMS** asset and click `Add` to create the asset + - Set values for the following attributes: + - `Charge efficiency (%)` (**e.g. 90**) + - `Discharge efficiency (%)` (**e.g. 90**) + - `Charge power maximum (kW)` (**e.g. 100**) + - `Discharge power maximum (kW)` (**e.g. -100**) + - `Energy capacity (kWh)` (**e.g. 400**) + - `Energy level percentage maximum (kW)` (**e.g. 90**) + - `Energy level percentage minimum (kW)` (**e.g. 10**) + - Connect the following attributes with your battery: + - `Energy level (kWh)` (Optional) + - `Energy level percentage (%)` + - `Power (kW)` + - Select the `Allow charging` and `Allow discharging` checkboxes to allow battery control by the EMS. Repeat the above steps to add multiple batteries to the EMS. @@ -82,64 +82,62 @@ Repeat the above steps to add multiple batteries to the EMS. For more information about battery attributes, see the [Ems Electricity Battery Asset](assets/EmsElectricityBatteryAsset.md). - ### 4) Connect EMS power attributes 1. Connect attributes: - * Go to the `Assets` page, select the **EMS** asset created in section 1 - * Connect the `Power net (kW)` attribute with your main power meter - * Connect the `Power flexible (kW)` attribute with the sum of the `Power (kW)` attribute of the battery assets created in section 3 using a Flow rule or a custom Groovy rule - * (Optional) Connect the `Power production (kW)`attribute with the sum of the `Power (kW)` attribute of power producing assets, such as solar panels, using a Flow rule or a custom Groovy rule - * Calculate the `Power consumption (kW)` = `Power net (kW)` - `Power flexible (kW)` - `Power production (kW)` using a Flow rule or a custom Groovy rule + - Go to the `Assets` page, select the **EMS** asset created in section 1 + - Connect the `Power net (kW)` attribute with your main power meter + - Connect the `Power flexible (kW)` attribute with the sum of the `Power (kW)` attribute of the battery assets created in section 3 using a Flow rule or a custom Groovy rule + - (Optional) Connect the `Power production (kW)`attribute with the sum of the `Power (kW)` attribute of power producing assets, such as solar panels, using a Flow rule or a custom Groovy rule + - Calculate the `Power consumption (kW)` = `Power net (kW)` - `Power flexible (kW)` - `Power production (kW)` using a Flow rule or a custom Groovy rule For more information on how to use rules, see [Rules and Forecasting](https://docs.openremote.io/docs/category/rules-and-forecasting). ### 5) Add power forecasts 1. Add power consumption forecast: - * Go to the `Assets` page, select the **EMS** asset created in section 1 - * Click `Modify` and select the `powerConsumption` attribute - * Click `Add configuration items`, select `Forecast` and click `Add` - * Click the new Forecast field and select `Forecast Configuration Weighted Exponential Average` - * Set the following values: - * Forecast Count = 672 - * Forecast Period = PT15M - * Past Count = 3 - * Past Period = P7D + - Go to the `Assets` page, select the **EMS** asset created in section 1 + - Click `Modify` and select the `powerConsumption` attribute + - Click `Add configuration items`, select `Forecast` and click `Add` + - Click the new Forecast field and select `Forecast Configuration Weighted Exponential Average` + - Set the following values: + - Forecast Count = 672 + - Forecast Period = PT15M + - Past Count = 3 + - Past Period = P7D Note: You can use a different forecast methods such as the `ML Forecasting Service`or your custom forecast method for the power consumption forecast. Step 2 is only required when there are power producing assets. - 2. (Optional) Add power production forecast: - * Link the sum of the `Power (kW)` attribute forecasts of power producing assets, such as solar panels, using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. + - Link the sum of the `Power (kW)` attribute forecasts of power producing assets, such as solar panels, using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. 3. (Optional) Add power flexible forecast: - * Link the sum of the `Power (kW)` attribute forecasts of `Ems Electricity Battery Asset` assets using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. + - Link the sum of the `Power (kW)` attribute forecasts of `Ems Electricity Battery Asset` assets using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. 4. (Optional) Add power net forecast: - * Calculate the `Power Net (kW)` = `Power consumption (kW)` + `Power flexible (kW)` + `Power production (kW)` forecast using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. - + - Calculate the `Power Net (kW)` = `Power consumption (kW)` + `Power flexible (kW)` + `Power production (kW)` forecast using a custom Groovy rule. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. ### 6) Add tariffs forecasts + Add the [ENTSO-E extension](https://github.com/openremote/extensions/tree/main/entsoe/) to your project dependencies. For more information on how to include extensions, see [Extensions](https://docs.openremote.io/docs/developer-guide/extensions). 1. Create asset: - * Go to the `Assets` page and click the `+` icon - * Select the `Entsoe agent` and name it (**e.g. ENTSO-E agent**) - * Click `Add` to create the asset + - Go to the `Assets` page and click the `+` icon + - Select the `Entsoe agent` and name it (**e.g. ENTSO-E agent**) + - Click `Add` to create the asset 2. Get EPEX spot prices: - * Follow the [ENTSO-E documentation](https://github.com/openremote/extensions/blob/main/entsoe/README.md) to connect the EPEX spot prices to a custom attribute (e.g. `nlPrices`) added to the `Entsoe agent` + - Follow the [ENTSO-E documentation](https://github.com/openremote/extensions/blob/main/entsoe/README.md) to connect the EPEX spot prices to a custom attribute (e.g. `nlPrices`) added to the `Entsoe agent` 3. Link EPEX spot prices to EMS: - * Link the `nlPrices` attribute forecast of the **Entsoe agent** to the `tariffImport` of the `Ems Energy Optimisation Asset` using a custom Groovy rule. Don't forget to make the conversion from €/MWh to €/kWh. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. - * Repeat the above step for the `tariffExport` of the `Ems Energy Optimisation Asset`, and additionally multiply the forecast prices with -1. + - Link the `nlPrices` attribute forecast of the **Entsoe agent** to the `tariffImport` of the `Ems Energy Optimisation Asset` using a custom Groovy rule. Don't forget to make the conversion from €/MWh to €/kWh. OpenRemote currently does not provide built-in functionality for linking/summing forecasts. + - Repeat the above step for the `tariffExport` of the `Ems Energy Optimisation Asset`, and additionally multiply the forecast prices with -1. ### 7) Select Optimisation method - * Go to the `Assets` page, select the **EMS** asset created in section 1 - * Find the `Optimisatiom method` attribute and select `Ems optimisation` from the dropdown menu. This will start the optimisation routine -The EMS is now set up and the optimisation is running. For a detailed explanation about the `Ems optimisation` method, see (Work in progress) +- Go to the `Assets` page, select the **EMS** asset created in section 1 +- Find the `Optimisatiom method` attribute and select `Ems optimisation` from the dropdown menu. This will start the optimisation routine +The EMS is now set up and the optimisation is running. For a detailed explanation about the `Ems optimisation` method, see (Work in progress) diff --git a/ems/docs/GopacsIntegration.md b/ems/docs/GopacsIntegration.md index dd1351f..b9046cd 100755 --- a/ems/docs/GopacsIntegration.md +++ b/ems/docs/GopacsIntegration.md @@ -31,7 +31,7 @@ To participate in GOPACS flex trading through OpenRemote, you need the following The following environment variables must be set on the OpenRemote manager: | Variable | Required | Description | -|-----------------------------------|----------|----------------------------------------------------------------------------------------------------------------| +| --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------- | | `GOPACS_PRIVATE_KEY_FILE` | Yes | File path to the Ed25519 private key for signing UFTP messages | | `GOPACS_CLIENT_ID` | Yes | OAuth2 client ID from GOPACS | | `GOPACS_CLIENT_SECRET` | Yes | OAuth2 client secret from GOPACS | @@ -177,7 +177,7 @@ when more than one matches. ### Configuration | Variable | Required | Description | -|-------------------------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ----------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GOPACS_REDISPATCH_API_KEY` | Yes | API key from GOPACS UI (User Menu > Settings > Generate API-key); required to resolve EAN effectivity per announcement. Polling will not start without it. | | `GOPACS_REDISPATCH_URL` | No | Base URL for the Redispatch API (default: `https://idcons.gopacs-services.eu`) | | `GOPACS_REDISPATCH_POLL_INTERVAL_MINUTES` | No | Polling interval in minutes (default: `5`, minimum: `5`) | @@ -188,7 +188,7 @@ On the `EMS GOPACS Asset` set the **`redispatchEnabled`** to `true` to start pol are read-only. | Group | Attribute | Value Type | Units | Read-only | Purpose | -|---------------|---------------------------------|-------------|---------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ------------- | ------------------------------- | ----------- | ------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Configuration | `redispatchEnabled` | Boolean | - | | Turn redispatch on/off | | Announcement | `redispatchAnnouncementId` | Text | - | ✓ | ID of the currently selected announcement, if any. | | Announcement | `redispatchComplianceType` | Text | - | ✓ | `MANDATORY` or `VOLUNTARY`. | @@ -222,12 +222,12 @@ are read-only. - The polling interval is clamped to a minimum of 5 minutes because GOPACS recommends spacing requests at least that far apart. - HTTP errors and exceptions on the announcements endpoint **skip the poll and preserve current attributes**, so - transient API hiccups do not flap the bid status. Any *successful* poll (HTTP 200) that yields no announcement + transient API hiccups do not flap the bid status. Any _successful_ poll (HTTP 200) that yields no announcement selected for the contracted EAN clears the active announcement and resets `redispatchBidStatus` to `NONE`. That covers three cases: the response is empty, the response has announcements but none are open `CONGESTIONMANAGEMENT`, or some are but the contracted EAN is not listed in their EAN-effectivity categories. Only a failed fetch (HTTP error / exception) leaves the previous announcement untouched. -- A *persistent* non-200 (e.g. an invalid API key returning 401, or a sustained outage) keeps the previously selected +- A _persistent_ non-200 (e.g. an invalid API key returning 401, or a sustained outage) keeps the previously selected announcement on screen indefinitely. If `redispatchLastPoll` falls behind the configured interval, check the manager logs for `Failed to fetch announcements: HTTP …` (warning) or `Error fetching announcements` (severe). - The handler **refuses to start** (logs `SEVERE`) when `GOPACS_REDISPATCH_API_KEY` is unset — without it there is no @@ -254,7 +254,7 @@ Announcement and bid history are stored as time-series data points on `redispatc `redispatchAnnouncementHistory` records **every** polled announcement on first sight (including ones that the EAN-effectivity check later rejects), so the audit trail captures everything GOPACS returned during the handler's -lifetime — not just the announcements that became active. When an announcement is then *selected* on a poll, a second, +lifetime — not just the announcements that became active. When an announcement is then _selected_ on a poll, a second, richer history entry is recorded with the matched effectivity details, so an active announcement will appear twice in the timeline (once at first sight, once on selection). To keep memory bounded for long-running handlers, the running set of already-recorded announcement IDs is capped at 10 000 entries (oldest inserted IDs are evicted first — @@ -279,4 +279,4 @@ see [Flex Trading with CSC and ATR (UFTP Messages)](https://www.gopacs.eu/wp-con #### Company Setup for Testing To configure your Trading Company for testing Capacity Steering Contracts, -follow: [Company Settings for CSC Participation](https://www.gopacs.eu/wp-content/uploads/2025/06/GOPACS-Company-settings-for-participating-in-CSC-Capacity-Steering-Contracts.pdf) \ No newline at end of file +follow: [Company Settings for CSC Participation](https://www.gopacs.eu/wp-content/uploads/2025/06/GOPACS-Company-settings-for-participating-in-CSC-Capacity-Steering-Contracts.pdf) diff --git a/ems/docs/assets/EmsDayAheadAsset.md b/ems/docs/assets/EmsDayAheadAsset.md index c4a06b3..6c7c50b 100755 --- a/ems/docs/assets/EmsDayAheadAsset.md +++ b/ems/docs/assets/EmsDayAheadAsset.md @@ -9,14 +9,14 @@ The `Ems Day Ahead Asset` must be a child asset of the `Ems Energy Optimisation ### Set attributes: | Attribute Name | Value Type | Units | Description | -|------------------------------|------------|-------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ---------------------------- | ---------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `collectTimeForecasts` | Text | - | Sets the time of day at which the forecast is collected from the `Ems Energy Optimisation Asset` for the `tariffExport` and `tariffImport` attributes. Example: 10:00 | | `useTariffDayAheadForecasts` | Boolean | - | When enabled, `tariffExportDayAheadForecast` and `tariffImportDayAheadForecast` are used as the day-ahead tariffs for optimisation instead of the `tariffExport` and `tariffImport` attributes from the `Ems Energy Optimisation Asset`. | ## Output attributes | Attribute Name | Value Type | Units | Description | -|--------------------------------|------------|-------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ------------------------------ | ---------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `lastUpdateForecasts` | Text | - | Displays when the forecasts for the `Ems Day Ahead Asset` were last updated | | `tariffExportDayAheadForecast` | Number | €/kWh | Stores the `tariffExport` attribute from the `Ems Energy Optimisation Asset` for the day-ahead at the time set by `collectTimeForecasts`. (Tariffs are stored directly in the database. The attribute value itself remains empty to prevent duplicate data points from being stored.) | | `tariffImportDayAheadForecast` | Number | €/kWh | Stores the `tariffImport` attribute from the `Ems Energy Optimisation Asset` for the day-ahead at the time set by `collectTimeForecasts`. (Tariffs are stored directly in the database. The attribute value itself remains empty to prevent duplicate data points from being stored.) | diff --git a/ems/docs/assets/EmsElectricityBatteryAsset.md b/ems/docs/assets/EmsElectricityBatteryAsset.md index dd95f87..2cfcef5 100755 --- a/ems/docs/assets/EmsElectricityBatteryAsset.md +++ b/ems/docs/assets/EmsElectricityBatteryAsset.md @@ -1,6 +1,6 @@ # Ems Electricity Battery Asset -For detailed setup instructions, see the [EMS Setup](../EmsSetup.md) guide. +For detailed setup instructions, see the [EMS Setup](../EmsSetup.md) guide. The `Ems Electricity Battery Asset` must be a child asset of the `Ems Energy Optimisation Asset` to be available as flexible power to the EMS. @@ -9,7 +9,7 @@ The `Ems Electricity Battery Asset` must be a child asset of the `Ems Energy Opt ### Set attributes: | Attribute Name | Value Type | Units | Description | -|--------------------------------|------------------|-------|------------------------------------------------------| +| ------------------------------ | ---------------- | ----- | ---------------------------------------------------- | | `allowCharging` | Boolean | - | Allow the EMS to control charging of the battery. | | `allowDischarging` | Boolean | - | Allow the EMS to control discharging of the battery. | | `chargeEfficiency` | Positive integer | % | Efficiency of the charging process. | @@ -23,7 +23,7 @@ The `Ems Electricity Battery Asset` must be a child asset of the `Ems Energy Opt ### Connect attributes: | Attribute Name | Value Type | Units | Description | -|-------------------------|-----------------|-------|-------------------------------------------------------------------| +| ----------------------- | --------------- | ----- | ----------------------------------------------------------------- | | `energyLevel` | Positive number | kWh | Current stored energy in the battery (Optional). | | `energyLevelPercentage` | Positive number | % | Current energy level percentage. | | `power` | Number | kW | Current power flow (positive = charging, negative = discharging). | @@ -31,6 +31,6 @@ The `Ems Electricity Battery Asset` must be a child asset of the `Ems Energy Opt ## Output attributes | Attribute Name | Value Type | Units | Description | -|--------------------|------------|-------|--------------------------------------------------------------| +| ------------------ | ---------- | ----- | ------------------------------------------------------------ | | `connectionStatus` | Enum | - | Current connection status of the battery. | | `powerSetpoint` | Number | kW | Current active battery power setpoint calculated by the EMS. | diff --git a/ems/docs/assets/EmsEnergyOptimisationAsset.md b/ems/docs/assets/EmsEnergyOptimisationAsset.md index 0b13305..c339937 100755 --- a/ems/docs/assets/EmsEnergyOptimisationAsset.md +++ b/ems/docs/assets/EmsEnergyOptimisationAsset.md @@ -9,7 +9,7 @@ The `Ems Energy Optimisation Asset` serves as the parent asset of the Energy Man ### Set attributes: | Attribute Name | Value Type | Units | Description | -|-----------------------------------------------|------------|-------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| --------------------------------------------- | ---------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `enableDetailedLogging` | Boolean | - | Enables detailed logging (optimisation method specific). | | `generatePowerLimitMaximumProfileManualInput` | Boolean | - | Generates maximum power limit profile based on the `powerLimitMaximumInput` value. | | `generatePowerLimitMinimumProfileManualInput` | Boolean | - | Generates minimum power limit profile based on the `powerLimitMinimumInput` value. | @@ -24,9 +24,8 @@ The `Ems Energy Optimisation Asset` serves as the parent asset of the Energy Man The exact way in which the following attributes are connected is left to the user. This is intentional and allows maximum flexibility when configuring the EMS based on the available power meter readings. The last column provides an example of how each attribute can be connected. Attribute values can be calculated using either _Flow rules_ or, for more advanced use cases, _Groovy rules_. Ensure that the `Rule state` configuration item is added to all attributes involved in the calculations. - | Attribute Name | Value Type | Units | Description | Connection example | -|---------------------|------------|-------|---------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| +| ------------------- | ---------- | ----- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `energyExportTotal` | Number | kWh | Total energy exported | Connect: main meter. (Not required for EMS optimisation) | | `energyImportTotal` | Number | kWh | Total energy imported | Connect: main meter. (Not required for EMS optimisation) | | `powerConsumption` | Number | kW | Current power consumption. | Calculate: sum of power consumer assets or, powerConsumption = powerNet - powerFlexible - powerProduction. | @@ -39,7 +38,7 @@ The exact way in which the following attributes are connected is left to the use ## Output attributes | Attribute Name | Value Type | Units | Description | -|----------------------------------|------------|-------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| -------------------------------- | ---------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `advancedSettingsAttributes` | Text | - | List of advanced settings attributes that can be added manually for the selected `optimisationMethod` (Only visible in `MODIFY` mode, optimisation method specific). | | `powerLimitMaximumProfileManual` | Number | kW | Current manual maximum power limit based on `powerLimitMaximumProfileManualInput`. | | `powerLimitMaximumProfileTotal` | Number | kW | Current total maximum power limit, calculated as the sum of the manual power limit and the GOPACS order power limit. This value is used for optimisation. |