mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[entsoe] Initial contribution (#17416)
Signed-off-by: Jørgen Melhus <jmelhus@outlook.com>
This commit is contained in:
parent
722818c30a
commit
99892c56eb
@ -104,6 +104,7 @@
|
|||||||
/bundles/org.openhab.binding.enigma2/ @gdolfen
|
/bundles/org.openhab.binding.enigma2/ @gdolfen
|
||||||
/bundles/org.openhab.binding.enocean/ @fruggy83
|
/bundles/org.openhab.binding.enocean/ @fruggy83
|
||||||
/bundles/org.openhab.binding.enphase/ @Hilbrand
|
/bundles/org.openhab.binding.enphase/ @Hilbrand
|
||||||
|
/bundles/org.openhab.binding.entsoe/ @jmelhus
|
||||||
/bundles/org.openhab.binding.enturno/ @klocsson
|
/bundles/org.openhab.binding.enturno/ @klocsson
|
||||||
/bundles/org.openhab.binding.ephemeris/ @clinique
|
/bundles/org.openhab.binding.ephemeris/ @clinique
|
||||||
/bundles/org.openhab.binding.epsonprojector/ @mlobstein
|
/bundles/org.openhab.binding.epsonprojector/ @mlobstein
|
||||||
|
@ -511,6 +511,11 @@
|
|||||||
<artifactId>org.openhab.binding.enphase</artifactId>
|
<artifactId>org.openhab.binding.enphase</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.binding.entsoe</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.addons.bundles</groupId>
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
<artifactId>org.openhab.binding.enturno</artifactId>
|
<artifactId>org.openhab.binding.enturno</artifactId>
|
||||||
|
13
bundles/org.openhab.binding.entsoe/NOTICE
Normal file
13
bundles/org.openhab.binding.entsoe/NOTICE
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This content is produced and maintained by the openHAB project.
|
||||||
|
|
||||||
|
* Project home: https://www.openhab.org
|
||||||
|
|
||||||
|
== Declared Project Licenses
|
||||||
|
|
||||||
|
This program and the accompanying materials are made available under the terms
|
||||||
|
of the Eclipse Public License 2.0 which is available at
|
||||||
|
https://www.eclipse.org/legal/epl-2.0/.
|
||||||
|
|
||||||
|
== Source Code
|
||||||
|
|
||||||
|
https://github.com/openhab/openhab-addons
|
85
bundles/org.openhab.binding.entsoe/README.md
Normal file
85
bundles/org.openhab.binding.entsoe/README.md
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# ENTSO-E Binding
|
||||||
|
|
||||||
|
This binding fetches day-ahead energy spot prices from ENTSO-E, the European Network of Transmission System Operators for Electricity.
|
||||||
|
|
||||||
|
Users can select a specific area to retrieve the relevant energy prices.
|
||||||
|
This binding helps users monitor and manage their energy consumption based on real-time pricing data.
|
||||||
|
It is recommended to use this binding together with a currency provider (e.g. [Freecurrency binding](https://www.openhab.org/addons/bindings/freecurrency/)) for exchanging euro spot prices to local currency.
|
||||||
|
|
||||||
|
## Supported Things
|
||||||
|
|
||||||
|
- `day-ahead`: This is the main and single Thing of the binding.
|
||||||
|
|
||||||
|
## Thing Configuration
|
||||||
|
|
||||||
|
To access the ENTSO-E Transparency Platform API, users need a **security token** for authentication and authorization.
|
||||||
|
This token ensures secure access to the platform's data and services.
|
||||||
|
For detailed instructions on obtaining this token, you can refer to the [ENTSO-E API Guide 2. Authentication and Authorisation](https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html#_authentication_and_authorisation).
|
||||||
|
|
||||||
|
Mandatory parameters of the Thing are security token and area.
|
||||||
|
Optional parameters are historic days, resolution, availability hour for day ahead spot prices and request timeout.
|
||||||
|
|
||||||
|
### `entsoe` Thing Configuration
|
||||||
|
|
||||||
|
| Name | Type | Description | Default | Required | Advanced |
|
||||||
|
|-------------------------------|-------------------|---------------------------------------------------------------------------|-----------|----------|----------|
|
||||||
|
| securityToken | text | Security token to fetch from ENTSO-E | N/A | yes | no |
|
||||||
|
| area | text | Area | N/A | yes | no |
|
||||||
|
| historicDays | integer | Historic days to get prices from (will use exchange rate as of today) | 0 | no | no |
|
||||||
|
| resolution | text | Data resolution | PT60M | no | no |
|
||||||
|
| spotPricesAvailableCetHour | integer | Which CET hour binding assumes new spot prices for next day is available | 13 | no | yes |
|
||||||
|
| requestTimeout | integer | Request timeout in seconds | 30 | no | yes |
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
|
||||||
|
Binding has one channel.
|
||||||
|
|
||||||
|
spot-price which are the values fetched from ENTSO-E and persisted in openHAB as time series.
|
||||||
|
The price is per kWh at your selected base currency.
|
||||||
|
|
||||||
|
| Channel | Type | Read/Write | Description |
|
||||||
|
|--------------------------|-----------------------|------------|-------------------------------------------|
|
||||||
|
| spot-price | Number:EnergyPrice | R | Spot prices |
|
||||||
|
|
||||||
|
### Thing Configuration
|
||||||
|
|
||||||
|
```java
|
||||||
|
Thing entsoe:day-ahead:eda "Entsoe Day Ahead" [ securityToken="your-security-token", area="10YNO-3--------J", historicDays=14 ]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Item Configuration
|
||||||
|
|
||||||
|
```java
|
||||||
|
Number:EnergyPrice energySpotPrice "Current Spot Price" <price> { channel="entsoe:day-ahead:eda:spot-price" }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Value-Added Tax
|
||||||
|
|
||||||
|
VAT is not included in any of the prices.
|
||||||
|
To include VAT for items linked to the `Number:EnergyPrice` channel, the [VAT profile](https://www.openhab.org/addons/transformations/vat/) can be used.
|
||||||
|
This must be installed separately.
|
||||||
|
Once installed, simply select "Value-Added Tax" as Profile when linking an item.
|
||||||
|
|
||||||
|
#### Total Price
|
||||||
|
|
||||||
|
_Please note:_ There is no channel providing the total price.
|
||||||
|
Instead, create a group item with `SUM` as aggregate function and add the individual price items as children.
|
||||||
|
Read more about how to in this similar binding [Energi Data Service](https://www.openhab.org/addons/bindings/energidataservice/#total-price)
|
||||||
|
|
||||||
|
### Trigger Channels
|
||||||
|
|
||||||
|
Channel `prices-received` is triggered when new prices are available.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
examples.rules
|
||||||
|
|
||||||
|
```java
|
||||||
|
rule "Spot prices received"
|
||||||
|
when
|
||||||
|
Channel "entsoe:day-ahead:eda:prices-received" triggered
|
||||||
|
then
|
||||||
|
// Do something within rule
|
||||||
|
logInfo("ENTSO-E Rule", "ENTSO-E channel triggered, new spot prices available")
|
||||||
|
end
|
||||||
|
```
|
17
bundles/org.openhab.binding.entsoe/pom.xml
Normal file
17
bundles/org.openhab.binding.entsoe/pom.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||||
|
<version>4.3.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>org.openhab.binding.entsoe</artifactId>
|
||||||
|
|
||||||
|
<name>openHAB Add-ons :: Bundles :: ENTSO-E Binding</name>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<features name="org.openhab.binding.entsoe-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||||
|
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||||
|
|
||||||
|
<feature name="openhab-binding-entsoe" description="ENTSO-E Binding" version="${project.version}">
|
||||||
|
<feature>openhab-runtime-base</feature>
|
||||||
|
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.entsoe/${project.version}</bundle>
|
||||||
|
</feature>
|
||||||
|
</features>
|
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.types.TimeSeries.Policy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EntsoeBindingConstants} class defines common constants, which are
|
||||||
|
* used across the whole binding.
|
||||||
|
*
|
||||||
|
* @author Jørgen Melhus - Initial contribution
|
||||||
|
* @author Miika Jukka - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EntsoeBindingConstants {
|
||||||
|
|
||||||
|
private static final String BINDING_ID = "entsoe";
|
||||||
|
|
||||||
|
public static final Policy TIMESERIES_POLICY = Policy.REPLACE;
|
||||||
|
|
||||||
|
public static final String CHANNEL_SPOT_PRICE = "spot-price";
|
||||||
|
|
||||||
|
public static final String CHANNEL_TRIGGER_PRICES_RECEIVED = "prices-received";
|
||||||
|
|
||||||
|
// Thing Type UIDs
|
||||||
|
public static final ThingTypeUID THING_TYPE_DAY_AHEAD = new ThingTypeUID(BINDING_ID, "day-ahead");
|
||||||
|
|
||||||
|
// List of all Thing Type UIDs
|
||||||
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_DAY_AHEAD);
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EntsoeConfiguration} class contains fields mapping thing configuration parameters.
|
||||||
|
*
|
||||||
|
* @author Jørgen Melhus - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EntsoeConfiguration {
|
||||||
|
public String securityToken = "";
|
||||||
|
public String area = "";
|
||||||
|
public int spotPricesAvailableCetHour = 13;
|
||||||
|
public int historicDays = 1;
|
||||||
|
public int requestTimeout = 30;
|
||||||
|
public String resolution = "PT60M";
|
||||||
|
}
|
@ -0,0 +1,273 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.entsoe.internal.client.Client;
|
||||||
|
import org.openhab.binding.entsoe.internal.client.Request;
|
||||||
|
import org.openhab.binding.entsoe.internal.client.SpotPrice;
|
||||||
|
import org.openhab.binding.entsoe.internal.exception.EntsoeConfigurationException;
|
||||||
|
import org.openhab.binding.entsoe.internal.exception.EntsoeResponseException;
|
||||||
|
import org.openhab.binding.entsoe.internal.exception.EntsoeResponseMapException;
|
||||||
|
import org.openhab.core.library.unit.Units;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.RefreshType;
|
||||||
|
import org.openhab.core.types.TimeSeries;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EntsoeHandler} is responsible for handling commands, which are
|
||||||
|
* sent to one of the channels.
|
||||||
|
*
|
||||||
|
* @author Jørgen Melhus - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EntsoeHandler extends BaseThingHandler {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(EntsoeHandler.class);
|
||||||
|
|
||||||
|
private EntsoeConfiguration config;
|
||||||
|
|
||||||
|
private @Nullable ScheduledFuture<?> refreshJob;
|
||||||
|
|
||||||
|
private Map<Instant, SpotPrice> entsoeTimeSeries = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private final ZoneId cetZoneId = ZoneId.of("CET");
|
||||||
|
|
||||||
|
private ZonedDateTime lastDayAheadReceived = ZonedDateTime.of(LocalDateTime.MIN, cetZoneId);
|
||||||
|
|
||||||
|
private int historicDaysInitially = 0;
|
||||||
|
|
||||||
|
public EntsoeHandler(Thing thing) {
|
||||||
|
super(thing);
|
||||||
|
config = new EntsoeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelLinked(ChannelUID channelUID) {
|
||||||
|
logger.trace("channelLinked(channelUID:{})", channelUID.getAsString());
|
||||||
|
String channelID = channelUID.getId();
|
||||||
|
|
||||||
|
if (EntsoeBindingConstants.CHANNEL_SPOT_PRICE.equals(channelID)) {
|
||||||
|
if (entsoeTimeSeries.isEmpty()) {
|
||||||
|
refreshPrices();
|
||||||
|
}
|
||||||
|
updateCurrentState(EntsoeBindingConstants.CHANNEL_SPOT_PRICE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
entsoeTimeSeries.clear();
|
||||||
|
ScheduledFuture<?> refreshJob = this.refreshJob;
|
||||||
|
if (refreshJob != null) {
|
||||||
|
refreshJob.cancel(true);
|
||||||
|
this.refreshJob = null;
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
logger.trace("handleCommand(channelUID:{}, command:{})", channelUID.getAsString(), command.toFullString());
|
||||||
|
|
||||||
|
if (command instanceof RefreshType) {
|
||||||
|
fetchNewPrices();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
config = getConfigAs(EntsoeConfiguration.class);
|
||||||
|
lastDayAheadReceived = ZonedDateTime.of(LocalDateTime.MIN, cetZoneId);
|
||||||
|
|
||||||
|
if (historicDaysInitially == 0) {
|
||||||
|
historicDaysInitially = config.historicDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLinked(EntsoeBindingConstants.CHANNEL_SPOT_PRICE)) {
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshJob = scheduler.schedule(this::refreshPrices, 0, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZonedDateTime currentCetTime() {
|
||||||
|
return ZonedDateTime.now(cetZoneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZonedDateTime currentCetTimeWholeHours() {
|
||||||
|
return currentCetTime().truncatedTo(ChronoUnit.HOURS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getMillisToNextStateUpdate() {
|
||||||
|
Instant now = Instant.now();
|
||||||
|
Instant nextHour = now.truncatedTo(ChronoUnit.HOURS).plus(1, ChronoUnit.HOURS);
|
||||||
|
long millis = Duration.between(now, nextHour).toMillis();
|
||||||
|
try {
|
||||||
|
Instant nextInstant = getNextSpotPrice().getInstant();
|
||||||
|
millis = Duration.between(now, nextInstant).toMillis();
|
||||||
|
} catch (EntsoeResponseMapException e) {
|
||||||
|
logger.warn("Using millis to next whole hour");
|
||||||
|
}
|
||||||
|
return millis + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SpotPrice getCurrentSpotPrice() throws EntsoeResponseMapException {
|
||||||
|
Duration duration = Duration.parse(config.resolution);
|
||||||
|
Instant now = Instant.now();
|
||||||
|
|
||||||
|
for (Map.Entry<Instant, SpotPrice> entry : entsoeTimeSeries.entrySet()) {
|
||||||
|
Instant entryStart = entry.getKey();
|
||||||
|
Instant entryEnd = entryStart.plus(duration);
|
||||||
|
|
||||||
|
if (!now.isBefore(entryStart) && now.isBefore(entryEnd)) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new EntsoeResponseMapException("Could not get current SpotPrice");
|
||||||
|
}
|
||||||
|
|
||||||
|
private SpotPrice getNextSpotPrice() throws EntsoeResponseMapException {
|
||||||
|
Instant now = Instant.now();
|
||||||
|
|
||||||
|
for (Map.Entry<Instant, SpotPrice> entry : entsoeTimeSeries.entrySet()) {
|
||||||
|
Instant entryStart = entry.getKey();
|
||||||
|
|
||||||
|
if (entryStart.isAfter(now)) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new EntsoeResponseMapException("Could not get next SpotPrice");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean needToFetchHistoricDays() {
|
||||||
|
return needToFetchHistoricDays(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean needToFetchHistoricDays(boolean updateHistoricDaysInitially) {
|
||||||
|
boolean needToFetch = false;
|
||||||
|
if (historicDaysInitially < config.historicDays) {
|
||||||
|
logger.debug("Need to fetch historic data. Historicdays was changed to a greater number: {}",
|
||||||
|
config.historicDays);
|
||||||
|
needToFetch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateHistoricDaysInitially && historicDaysInitially != config.historicDays) {
|
||||||
|
historicDaysInitially = config.historicDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
return needToFetch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshPrices() {
|
||||||
|
if (!isLinked(EntsoeBindingConstants.CHANNEL_SPOT_PRICE)) {
|
||||||
|
logger.debug("Channel {} is not linked, skipping channel update",
|
||||||
|
EntsoeBindingConstants.CHANNEL_SPOT_PRICE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entsoeTimeSeries.isEmpty()) {
|
||||||
|
fetchNewPrices();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean needsInitialUpdate = lastDayAheadReceived.equals(ZonedDateTime.of(LocalDateTime.MIN, cetZoneId))
|
||||||
|
|| needToFetchHistoricDays(true);
|
||||||
|
Instant nextDayInstant = ZonedDateTime.now(cetZoneId).plusDays(1).with(LocalTime.MIDNIGHT).toInstant();
|
||||||
|
boolean hasNextDayValue = entsoeTimeSeries.containsKey(nextDayInstant);
|
||||||
|
boolean readyForNextDayValue = currentCetTime()
|
||||||
|
.isAfter(currentCetTimeWholeHours().withHour(config.spotPricesAvailableCetHour));
|
||||||
|
|
||||||
|
if (needsInitialUpdate || (!hasNextDayValue && readyForNextDayValue)) {
|
||||||
|
fetchNewPrices();
|
||||||
|
} else {
|
||||||
|
updateCurrentState(EntsoeBindingConstants.CHANNEL_SPOT_PRICE);
|
||||||
|
schedule(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchNewPrices() {
|
||||||
|
logger.trace("Fetching new prices");
|
||||||
|
|
||||||
|
Instant startUtc = ZonedDateTime.now(cetZoneId)
|
||||||
|
.minusDays(needToFetchHistoricDays() ? config.historicDays - 1 : 0).with(LocalTime.MIDNIGHT)
|
||||||
|
.toInstant();
|
||||||
|
Instant endUtc = ZonedDateTime.now(cetZoneId).plusDays(2).with(LocalTime.MIDNIGHT).toInstant();
|
||||||
|
|
||||||
|
Request request = new Request(config.securityToken, config.area, startUtc, endUtc);
|
||||||
|
Client client = new Client();
|
||||||
|
boolean success = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
entsoeTimeSeries = client.doGetRequest(request, config.requestTimeout * 1000, config.resolution);
|
||||||
|
|
||||||
|
TimeSeries baseTimeSeries = new TimeSeries(EntsoeBindingConstants.TIMESERIES_POLICY);
|
||||||
|
for (Map.Entry<Instant, SpotPrice> entry : entsoeTimeSeries.entrySet()) {
|
||||||
|
baseTimeSeries.add(entry.getValue().getInstant(), entry.getValue().getState(Units.KILOWATT_HOUR));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
lastDayAheadReceived = currentCetTime();
|
||||||
|
sendTimeSeries(EntsoeBindingConstants.CHANNEL_SPOT_PRICE, baseTimeSeries);
|
||||||
|
updateCurrentState(EntsoeBindingConstants.CHANNEL_SPOT_PRICE);
|
||||||
|
triggerChannel(EntsoeBindingConstants.CHANNEL_TRIGGER_PRICES_RECEIVED);
|
||||||
|
success = true;
|
||||||
|
} catch (EntsoeResponseException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||||
|
} catch (EntsoeConfigurationException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
schedule(success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void schedule(boolean success) {
|
||||||
|
if (!success) {
|
||||||
|
logger.trace("schedule(success:{})", success);
|
||||||
|
refreshJob = scheduler.schedule(this::refreshPrices, 5, TimeUnit.MINUTES);
|
||||||
|
} else {
|
||||||
|
refreshJob = scheduler.schedule(this::refreshPrices, getMillisToNextStateUpdate(), TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCurrentState(String channelID) {
|
||||||
|
logger.trace("Updating current state");
|
||||||
|
try {
|
||||||
|
updateState(channelID, getCurrentSpotPrice().getState(Units.KILOWATT_HOUR));
|
||||||
|
} catch (EntsoeConfigurationException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.entsoe.internal.EntsoeBindingConstants.*;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EntsoeHandlerFactory} is responsible for creating things and thing
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* @author Jørgen Melhus - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(configurationPid = "binding.entsoe", service = ThingHandlerFactory.class)
|
||||||
|
public class EntsoeHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
|
return SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||||
|
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
|
if (THING_TYPE_DAY_AHEAD.equals(thingTypeUID)) {
|
||||||
|
return new EntsoeHandler(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.entsoe.internal.exception.EntsoeConfigurationException;
|
||||||
|
import org.openhab.binding.entsoe.internal.exception.EntsoeResponseException;
|
||||||
|
import org.openhab.core.io.net.http.HttpUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Miika Jukka - Initial contribution
|
||||||
|
* @author Jørgen Melhus - Contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class Client {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(Client.class);
|
||||||
|
|
||||||
|
private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
|
||||||
|
public Map<Instant, SpotPrice> doGetRequest(Request request, int timeout, String configResolution)
|
||||||
|
throws EntsoeResponseException, EntsoeConfigurationException {
|
||||||
|
try {
|
||||||
|
logger.debug("Sending GET request with parameters: {}", request);
|
||||||
|
String url = request.toUrl();
|
||||||
|
String responseText = HttpUtil.executeUrl("GET", url, timeout);
|
||||||
|
if (responseText == null) {
|
||||||
|
throw new EntsoeResponseException("Request failed");
|
||||||
|
}
|
||||||
|
logger.trace("Response: {}", responseText);
|
||||||
|
return parseXmlResponse(responseText, configResolution);
|
||||||
|
} catch (IOException e) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message != null && message.contains("Authentication challenge without WWW-Authenticate header")) {
|
||||||
|
throw new EntsoeConfigurationException("Authentication failed. Please check your security token");
|
||||||
|
}
|
||||||
|
throw new EntsoeResponseException(e);
|
||||||
|
} catch (ParserConfigurationException | SAXException e) {
|
||||||
|
throw new EntsoeResponseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Instant, SpotPrice> parseXmlResponse(String responseText, String configResolution)
|
||||||
|
throws ParserConfigurationException, SAXException, IOException, EntsoeResponseException,
|
||||||
|
EntsoeConfigurationException {
|
||||||
|
Map<Instant, SpotPrice> responseMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||||
|
Document document = documentBuilder.parse(new InputSource(new StringReader(responseText)));
|
||||||
|
document.getDocumentElement().normalize();
|
||||||
|
|
||||||
|
// Check for rejection
|
||||||
|
if (document.getDocumentElement().getNodeName().equals("Acknowledgement_MarketDocument")) {
|
||||||
|
NodeList reasonOfRejection = document.getElementsByTagName("Reason");
|
||||||
|
Node reasonNode = reasonOfRejection.item(0);
|
||||||
|
Element reasonElement = (Element) reasonNode;
|
||||||
|
String reasonCode = reasonElement.getElementsByTagName("code").item(0).getTextContent();
|
||||||
|
String reasonText = reasonElement.getElementsByTagName("text").item(0).getTextContent();
|
||||||
|
throw new EntsoeResponseException(
|
||||||
|
String.format("Request failed with API response: Code %s, Text %s", reasonCode, reasonText));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all "timeSeries" nodes from document
|
||||||
|
NodeList listOfTimeSeries = document.getElementsByTagName("TimeSeries");
|
||||||
|
boolean resolutionFound = false;
|
||||||
|
for (int i = 0; i < listOfTimeSeries.getLength(); i++) {
|
||||||
|
Node timeSeriesNode = listOfTimeSeries.item(i);
|
||||||
|
if (timeSeriesNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
Element timeSeriesElement = (Element) timeSeriesNode;
|
||||||
|
|
||||||
|
String currency = timeSeriesElement.getElementsByTagName("currency_Unit.name").item(0).getTextContent();
|
||||||
|
String measureUnit = timeSeriesElement.getElementsByTagName("price_Measure_Unit.name").item(0)
|
||||||
|
.getTextContent();
|
||||||
|
|
||||||
|
NodeList listOfPeriod = timeSeriesElement.getElementsByTagName("Period");
|
||||||
|
Node periodNode = listOfPeriod.item(0);
|
||||||
|
Element periodElement = (Element) periodNode;
|
||||||
|
|
||||||
|
NodeList listOfTimeInterval = periodElement.getElementsByTagName("timeInterval");
|
||||||
|
Node startTimeNode = listOfTimeInterval.item(0);
|
||||||
|
Element startTimeElement = (Element) startTimeNode;
|
||||||
|
String startTime = startTimeElement.getElementsByTagName("start").item(0).getTextContent();
|
||||||
|
Instant startTimeInstant = ZonedDateTime.parse(startTime, DateTimeFormatter.ISO_ZONED_DATE_TIME)
|
||||||
|
.toInstant();
|
||||||
|
Node endTimeNode = listOfTimeInterval.item(0);
|
||||||
|
Element endTimeElement = (Element) endTimeNode;
|
||||||
|
String endTime = endTimeElement.getElementsByTagName("end").item(0).getTextContent();
|
||||||
|
Instant endTimeInstant = ZonedDateTime.parse(endTime, DateTimeFormatter.ISO_ZONED_DATE_TIME)
|
||||||
|
.toInstant();
|
||||||
|
|
||||||
|
String resolution = periodElement.getElementsByTagName("resolution").item(0).getTextContent();
|
||||||
|
Duration durationResolution = Duration.parse(resolution);
|
||||||
|
Duration durationTotal = Duration.between(startTimeInstant, endTimeInstant);
|
||||||
|
int numberOfDurations = Math.round(durationTotal.toMinutes() / durationResolution.toMinutes());
|
||||||
|
|
||||||
|
logger.debug("\"timeSeries\" node: {}/{} with start time: {} and resolution {}", (i + 1),
|
||||||
|
listOfTimeSeries.getLength(), startTimeInstant.atZone(ZoneId.of("UTC")), resolution);
|
||||||
|
|
||||||
|
NodeList listOfPoints = periodElement.getElementsByTagName("Point");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EntsoE changed their API on October 1 2024 so that they use the A03 curve type instead of A01. The
|
||||||
|
* difference between these curve types is that in A03 they don’t repeat an hour if it has the same
|
||||||
|
* price
|
||||||
|
* as the previous hour.
|
||||||
|
*/
|
||||||
|
int pointNr = 0;
|
||||||
|
for (int p = 0; p < numberOfDurations && resolution.equalsIgnoreCase(configResolution); p++) {
|
||||||
|
resolutionFound = true;
|
||||||
|
Node pointNode = listOfPoints.item(pointNr);
|
||||||
|
|
||||||
|
int multiplier = p;
|
||||||
|
if (pointNode != null) {
|
||||||
|
Element pointElement = (Element) pointNode;
|
||||||
|
String price = pointElement.getElementsByTagName("price.amount").item(0).getTextContent();
|
||||||
|
Double priceAsDouble = Double.parseDouble(price);
|
||||||
|
SpotPrice t = new SpotPrice(currency, measureUnit, priceAsDouble, startTimeInstant, multiplier,
|
||||||
|
resolution);
|
||||||
|
responseMap.put(t.getInstant(), t);
|
||||||
|
logger.trace("\"Point\" node: {}/{} with values: {} - {} {}/{}", (p + 1), numberOfDurations,
|
||||||
|
t.getInstant(), priceAsDouble, currency, measureUnit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node nextPointNode = listOfPoints.item(pointNr + 1);
|
||||||
|
if (nextPointNode != null) {
|
||||||
|
Element nextPointElement = (Element) nextPointNode;
|
||||||
|
Node nextPositionNode = nextPointElement.getElementsByTagName("position").item(0);
|
||||||
|
if (nextPositionNode != null) {
|
||||||
|
int nextPosition = Integer.parseInt(nextPositionNode.getTextContent());
|
||||||
|
if (nextPosition == p + 2) {
|
||||||
|
pointNr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!resolutionFound) {
|
||||||
|
throw new EntsoeConfigurationException("Resolution " + configResolution + " not found in ENTSOE response");
|
||||||
|
}
|
||||||
|
return responseMap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal.client;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Miika Jukka - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class Request {
|
||||||
|
|
||||||
|
private static DateTimeFormatter requestFormat = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
|
||||||
|
|
||||||
|
private static final String BASE_URL = "https://web-api.tp.entsoe.eu/api?";
|
||||||
|
private static final String DOCUMENT_TYPE_PRICE = "A44";
|
||||||
|
|
||||||
|
private final String securityToken;
|
||||||
|
private final String area;
|
||||||
|
private final Instant periodStart;
|
||||||
|
private final Instant periodEnd;
|
||||||
|
|
||||||
|
public Request(String securityToken, String area, Instant periodStart, Instant periodEnd) {
|
||||||
|
this.securityToken = securityToken;
|
||||||
|
this.area = area;
|
||||||
|
this.periodStart = periodStart;
|
||||||
|
this.periodEnd = periodEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toUrl() {
|
||||||
|
return urlBuilder(this.securityToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return urlBuilder("xxxxx-xxxxx-xxxxx");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String urlBuilder(String securityToken) {
|
||||||
|
StringBuilder urlBuilder = new StringBuilder(BASE_URL).append("securityToken=").append(securityToken)
|
||||||
|
.append("&documentType=").append(DOCUMENT_TYPE_PRICE).append("&in_domain=").append(area)
|
||||||
|
.append("&out_domain=").append(area).append("&periodStart=")
|
||||||
|
.append(periodStart.atZone(ZoneOffset.UTC).format(requestFormat)).append("&periodEnd=")
|
||||||
|
.append(periodEnd.atZone(ZoneOffset.UTC).format(requestFormat));
|
||||||
|
return urlBuilder.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal.client;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.Period;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
|
|
||||||
|
import javax.measure.Unit;
|
||||||
|
import javax.measure.quantity.Energy;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.entsoe.internal.exception.EntsoeResponseException;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.library.types.QuantityType;
|
||||||
|
import org.openhab.core.library.unit.Units;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jørgen Melhus - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SpotPrice {
|
||||||
|
private String currency;
|
||||||
|
private Unit<Energy> unit;
|
||||||
|
private Double price;
|
||||||
|
private Instant time;
|
||||||
|
|
||||||
|
public SpotPrice(String currency, String unit, Double price, Instant start, int iteration, String resolution)
|
||||||
|
throws EntsoeResponseException {
|
||||||
|
this.currency = currency;
|
||||||
|
this.unit = convertEntsoeUnit(unit);
|
||||||
|
this.price = price;
|
||||||
|
this.time = calculateDateTime(start, iteration, resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instant calculateDateTime(Instant start, int iteration, String resolution) throws EntsoeResponseException {
|
||||||
|
try {
|
||||||
|
if (resolution.toUpperCase().startsWith("PT")) {
|
||||||
|
Duration d = Duration.parse(resolution).multipliedBy(iteration);
|
||||||
|
return start.plus(d);
|
||||||
|
} else if (resolution.toUpperCase().startsWith("P1")) {
|
||||||
|
return start.plus(Period.parse(resolution).multipliedBy(iteration));
|
||||||
|
}
|
||||||
|
throw new EntsoeResponseException("Unknown resolution: " + resolution);
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
|
throw new EntsoeResponseException(
|
||||||
|
"DateTimeParseException (ENTSOE resolution: " + resolution + "): " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Unit<Energy> convertEntsoeUnit(String unit) throws EntsoeResponseException {
|
||||||
|
if ("MWh".equalsIgnoreCase(unit)) {
|
||||||
|
return Units.MEGAWATT_HOUR;
|
||||||
|
}
|
||||||
|
if ("kWh".equalsIgnoreCase(unit)) {
|
||||||
|
return Units.KILOWATT_HOUR;
|
||||||
|
}
|
||||||
|
if ("Wh".equalsIgnoreCase(unit)) {
|
||||||
|
return Units.WATT_HOUR;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new EntsoeResponseException("Unit from ENTSO-E is unknown: " + unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal getPrice(Unit<Energy> toUnit) {
|
||||||
|
return new DecimalType(toUnit.getConverterTo(this.unit).convert(this.price)).toBigDecimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getState(Unit<Energy> toUnit) {
|
||||||
|
try {
|
||||||
|
return new QuantityType<>(getPrice(toUnit) + " " + this.currency + "/" + toUnit.toString());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return new DecimalType(getPrice(toUnit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getInstant() {
|
||||||
|
return this.time;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal.exception;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Miika Jukka - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EntsoeConfigurationException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public EntsoeConfigurationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntsoeConfigurationException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal.exception;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Miika Jukka - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EntsoeResponseException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public EntsoeResponseException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntsoeResponseException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntsoeResponseException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.entsoe.internal.exception;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jørgen Melhus - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EntsoeResponseMapException extends EntsoeConfigurationException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public EntsoeResponseMapException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntsoeResponseMapException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<addon:addon id="entsoe" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||||
|
|
||||||
|
<type>binding</type>
|
||||||
|
<name>ENTSO-E Binding</name>
|
||||||
|
<description>This is the binding for European Network of Transmission System Operators (ENTSO-E) to receive electricity
|
||||||
|
market spot prices.
|
||||||
|
</description>
|
||||||
|
<connection>cloud</connection>
|
||||||
|
|
||||||
|
</addon:addon>
|
@ -0,0 +1,142 @@
|
|||||||
|
# add-on
|
||||||
|
|
||||||
|
addon.entsoe.name = ENTSO-E Binding
|
||||||
|
addon.entsoe.description = This is the binding for European Network of Transmission System Operators (ENTSO-E) to receive electricity market spot prices.
|
||||||
|
|
||||||
|
# thing types
|
||||||
|
|
||||||
|
thing-type.entsoe.day-ahead.label = Day-ahead prices
|
||||||
|
thing-type.entsoe.day-ahead.description = Hourly spot prices of the electricity market from European Network of Transmission System Operators (ENTSO-E)
|
||||||
|
|
||||||
|
# thing types config
|
||||||
|
|
||||||
|
thing-type.config.entsoe.day-ahead.area.label = Area
|
||||||
|
thing-type.config.entsoe.day-ahead.area.description = Choose from list or enter custom EIC area. See https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html#_areas and https://www.entsoe.eu/data/energy-identification-codes-eic/eic-approved-codes/ (Area Y, Bidding Zone)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A39I = EE Estonia
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A44P = SE1 Swedish Elspot Area 1
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A45N = SE2 Swedish Elspot Area 2
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A46L = SE3 Swedish Elspot Area 3
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A47J = SE4 Swedish Elspot Area 4
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A48H = NO5 Norwegian Area Elspot Area 5
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A50U = KALININGRAD_AREA Kaliningrad area
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A51S = BELARUS_AREA Belarus area
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A55K = LT_BEL_IMP_AREA Lithuania-Belarus Import Area
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A56I = LT_BEL_EXP_AREA Lithuania-Belarus Export Area
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A57G = GB_N2EX_PRICZONE Bidding Zone - Great Britain (N2EX)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A58E = GB_APX_PRICEZONE Bidding Zone - Great Britain (APX)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A59C = SEM Ireland and Northern Ireland
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A63L = DE_AT_LU Germany_Austria_Luxemburg
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A66F = IT-GR Italy Greece
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A67D = IT-NORTH_SI Italy North_Slovenia
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A68B = IT-NORTH_CH Italy North_Switzerland
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A70O = IT-CENTRE_NORTH Italy Centre-North
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A71M = IT-CENTRE_SOUTH Italy Centre-South
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A73I = IT-NORTH Italy North
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A74G = IT-SARDINIA Italy Sardinia
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A75E = IT-SICILY Italy Sicily
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A788 = IT-SOUTH Italy South
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A80L = IT-NORTH_AT Italy North_Austria
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A81J = IT-NORTH_FR Italy North_France
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A82H = DE_LU Germany_Luxemburg
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A877 = IT-MT Italy_Malta
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A885 = IT-SACO_AC Italy Saco_AC
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A893 = IT-SACODC Italy Saco_DC
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A90I = IT-MONFALCONE Italy Monfalcone
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A958 = ICELAND Member State Iceland
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001A1001A990 = MOLDOVA Moldova
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001C--000182 = UA-IPS Ukraine IPS
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001C--000611 = IT-MONTENEGRO BIDDING ZONE ITALY-MONTENEGRO
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001C--00096J = IT-CALABRIA Italy Calabria
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001C--00098F = FR_UK_IFA_BZN IFA Bidding Zone
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001C--00100H = CAKOSTT Control Area Kosovo*
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001C--001219 = NO_FICT_AREA_2A Norwegian Fictitious Area 2A
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001C--00146U = NORDIC_SYNC_AREA Nordic Synchronous Area
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10Y1001C--00148Q = SE3A Swedish Fictitious Area SE3A
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YAL-KESH-----5 = AL Albania
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YAT-APG------L = AT Austria
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YBA-JPCC-----D = BA Bosnia and Herzegovina
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YBE----------2 = BE Belgium
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YCA-BULGARIA-R = BG Bulgaria
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YCH-SWISSGRIDZ = CH Switzerland
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YCS-CG-TSO---S = ME Montenegro
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YCS-SERBIATSOV = RS Serbia
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YCY-1001A0003J = CY Cyprus
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YCZ-CEPS-----N = CZ Czech Republic
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YDK-1--------W = DK1 Denmark DK1
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YDK-2--------M = DK2 Denmark DK2
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YES-REE------0 = ES Spain
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YFI-1--------U = FI Finland
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YFR-RTE------C = FR France
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YGB----------A = GB Great Britain
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YGR-HTSO-----Y = GR Greece
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YHR-HEP------M = HR Croatia
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YHU-MAVIR----U = HU Hungary
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YLT-1001A0008Q = LT Lithuania
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YLV-1001A00074 = LV Latvia
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YMK-MEPSO----8 = MK FYROM
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YNL----------L = NL Netherlands
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YNO-1--------2 = NO1 Norwegian Area Elspot Area 1
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YNO-2--------T = NO2 Norwegian Area Elspot Area 2
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YNO-3--------J = NO3 Norwegian Area Elspot Area 3
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YNO-4--------9 = NO4 Norwegian Area Elspot Area 4
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YPL-AREA-----S = PL Poland
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YPT-REN------W = PT Portugal
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YRO-TEL------P = RO Romania
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YSI-ELES-----O = SI Slovenia
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YSK-SEPS-----K = SK Slovak Republic
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YTR-TEIAS----W = TEIAS_AREA TEIAS Area
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.10YUA-WEPS-----0 = UA-BEI Ukrainian Area of Burshtyn island
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.14Y----0000041-W = AT-EXAAMC Virtual Bidding Zone EXAA
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.14YCCPADATENMLDW = CCPACCP1 CCP Austria GmbH
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.14YEXAADATENMLDU = EXAACCP EXAA Abwicklungsstelle für Energieprodukte AG
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y000000930808E = FR_UK_IFA2000_1 vhub_UK_IFA2000_link1
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y000000930809C = FR_FR_IFA2000_1 vhub_FR_IFA2000_link1
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y000000930810R = FR_UK_IFA2000_2 vhub_UK_IFA2000_link2
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y000000930811P = FR_FR_IFA2000_2 vhub_FR_IFA2000_link2
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y000000930814J = FR_UK_IFA2 vhub_UK_IFA2
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y000000930815H = FR_FR_IFA2 vhub_FR_IFA2
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y000000930816F = FR_IT_SAV_PIE vhub_IT_Savoie_Piemont
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y000000930817D = FR_FR_SAV_PIE vhub_FR_Savoie_Piemont
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.17Y0000009369493 = FR_UK_IFA2_V_BZN IFA2_virtual_BZN
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.44Y-00000000160K = FI_FS Fingrid Oyj
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.44Y-00000000161I = FI_EL Fingrid Oyj
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.45Y000000000001C = DKW-NO2 DKW-NO2 virtual Bidding Zone Border
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.45Y000000000002A = DKW-SE3 DKW-SE3 virtual Bidding Zone Border
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.45Y0000000000038 = DKW-DKE DKW-DKE virtual Bidding Zone Border
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000001Y = SE3_FS SE3_FS (SE3, Fennoskan)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000002W = SE3_KS SE3_KS (SE3, Kontiskan)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000003U = SE4_SP SE4_SP (SE4, Swepol link)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000004S = SE4_NB SE4_NB (SE4, Nordbalt)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000005Q = SE4_BC SE4_BC (SE4, Baltic Cable)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000007M = CUT_AREA_SE3LS Cut area SE3LS
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000008K = CUT_AREA_SE3 Cut area SE3
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000009I = CUTCOR_SE3LS-SE3 Cut corridor SE3LS-SE3
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000015N = VBZ_SE3-SE4_ACSW Virtual bidding zone border SE3-SE4 AC+SWL
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000016L = VBZ_SE4-SE3_ACSW Virtual bidding zone border SE4-SE3 AC+SWL
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000017J = SE3_SWL SE3_SWL (SE3, Sydvastlanken)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000018H = SE4_SWL SE4_SWL (SE4, Sydvastlanken)
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.46Y000000000019F = CUTCOR_SE3A-SE3 Cut corridor SE3A-SE3
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.50Y0JVU59B4JWQCU = NO_NO2NSL Virtual Bidding Zone NO2NSL
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.50Y73EMZ34CQL9AJ = NO_NO2_NL Virtual Bidding Zone NO2-NL
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.50YCUY85S1HH29EK = NO_NO2_DK1 Virtual Bidding Zone NO2-DK1
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.50Y-HTS3792HUOAC = NO_NO2-GB Virtual bidding Zone NO2-GB
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.50YNBFFTWZRAHA3P = NO_NO2-DE Virtual bidding Zone NO2-DE
|
||||||
|
thing-type.config.entsoe.day-ahead.area.option.67Y-VISKOL-2003T = VISKOL2003 VISKOL DOO NOVI SAD SRBIJA
|
||||||
|
thing-type.config.entsoe.day-ahead.historicDays.label = Historic Data
|
||||||
|
thing-type.config.entsoe.day-ahead.historicDays.description = Specify number of days to download historic data of energy spot prices (historic data will get exchange rate of today)
|
||||||
|
thing-type.config.entsoe.day-ahead.requestTimeout.label = Request Timeout
|
||||||
|
thing-type.config.entsoe.day-ahead.requestTimeout.description = Request timeout value in seconds
|
||||||
|
thing-type.config.entsoe.day-ahead.resolution.label = Resolution
|
||||||
|
thing-type.config.entsoe.day-ahead.resolution.description = Data resolution
|
||||||
|
thing-type.config.entsoe.day-ahead.resolution.option.PT15M = 15 minutes
|
||||||
|
thing-type.config.entsoe.day-ahead.resolution.option.PT30M = 30 minutes
|
||||||
|
thing-type.config.entsoe.day-ahead.resolution.option.PT60M = 60 minutes
|
||||||
|
thing-type.config.entsoe.day-ahead.securityToken.label = Security Token
|
||||||
|
thing-type.config.entsoe.day-ahead.securityToken.description = Security token for ENTSO-E API
|
||||||
|
thing-type.config.entsoe.day-ahead.spotPricesAvailableCetHour.label = Publish Hour
|
||||||
|
thing-type.config.entsoe.day-ahead.spotPricesAvailableCetHour.description = CET hour spot prices will get published on ENTSO-E. Usually at 13 CET.
|
||||||
|
|
||||||
|
# channel types
|
||||||
|
|
||||||
|
channel-type.entsoe.spot-price.label = Spot Price
|
||||||
|
channel-type.entsoe.trigger-prices-received.label = Trigger Prices Received
|
@ -0,0 +1,194 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="entsoe"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<thing-type id="day-ahead">
|
||||||
|
|
||||||
|
<label>Day-ahead prices</label>
|
||||||
|
<description>Hourly spot prices of the electricity market from European Network of Transmission System Operators
|
||||||
|
(ENTSO-E)</description>
|
||||||
|
<category>WebService</category>
|
||||||
|
<channels>
|
||||||
|
<channel id="spot-price" typeId="spot-price"/>
|
||||||
|
<channel id="prices-received" typeId="trigger-prices-received"/>
|
||||||
|
</channels>
|
||||||
|
|
||||||
|
<config-description>
|
||||||
|
<parameter name="securityToken" type="text" required="true">
|
||||||
|
<context>password</context>
|
||||||
|
<label>Security Token</label>
|
||||||
|
<description>Security token for ENTSO-E API</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="area" type="text" required="true">
|
||||||
|
<label>Area</label>
|
||||||
|
<description>Choose from list or enter custom EIC area. See
|
||||||
|
https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html#_areas
|
||||||
|
and
|
||||||
|
https://www.entsoe.eu/data/energy-identification-codes-eic/eic-approved-codes/ (Area Y, Bidding Zone)</description>
|
||||||
|
<limitToOptions>false</limitToOptions>
|
||||||
|
<options>
|
||||||
|
<!--
|
||||||
|
url: https://www.entsoe.eu/data/energy-identification-codes-eic/eic-approved-codes/
|
||||||
|
filter:
|
||||||
|
EIC Type Code: Area Y
|
||||||
|
Function: Bidding Zone
|
||||||
|
-->
|
||||||
|
<option value="10Y1001A1001A39I">EE Estonia</option>
|
||||||
|
<option value="10Y1001A1001A44P">SE1 Swedish Elspot Area 1</option>
|
||||||
|
<option value="10Y1001A1001A45N">SE2 Swedish Elspot Area 2</option>
|
||||||
|
<option value="10Y1001A1001A46L">SE3 Swedish Elspot Area 3</option>
|
||||||
|
<option value="10Y1001A1001A47J">SE4 Swedish Elspot Area 4</option>
|
||||||
|
<option value="10Y1001A1001A48H">NO5 Norwegian Area Elspot Area 5</option>
|
||||||
|
<option value="10Y1001A1001A50U">KALININGRAD_AREA Kaliningrad area</option>
|
||||||
|
<option value="10Y1001A1001A51S">BELARUS_AREA Belarus area</option>
|
||||||
|
<option value="10Y1001A1001A55K">LT_BEL_IMP_AREA Lithuania-Belarus Import Area</option>
|
||||||
|
<option value="10Y1001A1001A56I">LT_BEL_EXP_AREA Lithuania-Belarus Export Area</option>
|
||||||
|
<option value="10Y1001A1001A57G">GB_N2EX_PRICZONE Bidding Zone - Great Britain (N2EX)</option>
|
||||||
|
<option value="10Y1001A1001A58E">GB_APX_PRICEZONE Bidding Zone - Great Britain (APX)</option>
|
||||||
|
<option value="10Y1001A1001A59C">SEM Ireland and Northern Ireland</option>
|
||||||
|
<option value="10Y1001A1001A63L">DE_AT_LU Germany_Austria_Luxemburg</option>
|
||||||
|
<option value="10Y1001A1001A66F">IT-GR Italy Greece</option>
|
||||||
|
<option value="10Y1001A1001A67D">IT-NORTH_SI Italy North_Slovenia</option>
|
||||||
|
<option value="10Y1001A1001A68B">IT-NORTH_CH Italy North_Switzerland</option>
|
||||||
|
<option value="10Y1001A1001A70O">IT-CENTRE_NORTH Italy Centre-North</option>
|
||||||
|
<option value="10Y1001A1001A71M">IT-CENTRE_SOUTH Italy Centre-South</option>
|
||||||
|
<option value="10Y1001A1001A73I">IT-NORTH Italy North</option>
|
||||||
|
<option value="10Y1001A1001A74G">IT-SARDINIA Italy Sardinia</option>
|
||||||
|
<option value="10Y1001A1001A75E">IT-SICILY Italy Sicily</option>
|
||||||
|
<option value="10Y1001A1001A788">IT-SOUTH Italy South</option>
|
||||||
|
<option value="10Y1001A1001A80L">IT-NORTH_AT Italy North_Austria</option>
|
||||||
|
<option value="10Y1001A1001A81J">IT-NORTH_FR Italy North_France</option>
|
||||||
|
<option value="10Y1001A1001A82H">DE_LU Germany_Luxemburg</option>
|
||||||
|
<option value="10Y1001A1001A877">IT-MT Italy_Malta</option>
|
||||||
|
<option value="10Y1001A1001A885">IT-SACO_AC Italy Saco_AC</option>
|
||||||
|
<option value="10Y1001A1001A893">IT-SACODC Italy Saco_DC</option>
|
||||||
|
<option value="10Y1001A1001A90I">IT-MONFALCONE Italy Monfalcone</option>
|
||||||
|
<option value="10Y1001A1001A958">ICELAND Member State Iceland</option>
|
||||||
|
<option value="10Y1001A1001A990">MOLDOVA Moldova</option>
|
||||||
|
<option value="10Y1001C--000182">UA-IPS Ukraine IPS</option>
|
||||||
|
<option value="10Y1001C--000611">IT-MONTENEGRO BIDDING ZONE ITALY-MONTENEGRO</option>
|
||||||
|
<option value="10Y1001C--00096J">IT-CALABRIA Italy Calabria</option>
|
||||||
|
<option value="10Y1001C--00098F">FR_UK_IFA_BZN IFA Bidding Zone</option>
|
||||||
|
<option value="10Y1001C--00100H">CAKOSTT Control Area Kosovo*</option>
|
||||||
|
<option value="10Y1001C--001219">NO_FICT_AREA_2A Norwegian Fictitious Area 2A</option>
|
||||||
|
<option value="10Y1001C--00146U">NORDIC_SYNC_AREA Nordic Synchronous Area</option>
|
||||||
|
<option value="10Y1001C--00148Q">SE3A Swedish Fictitious Area SE3A</option>
|
||||||
|
<option value="10YAL-KESH-----5">AL Albania</option>
|
||||||
|
<option value="10YAT-APG------L">AT Austria</option>
|
||||||
|
<option value="10YBA-JPCC-----D">BA Bosnia and Herzegovina</option>
|
||||||
|
<option value="10YBE----------2">BE Belgium</option>
|
||||||
|
<option value="10YCA-BULGARIA-R">BG Bulgaria</option>
|
||||||
|
<option value="10YCH-SWISSGRIDZ">CH Switzerland</option>
|
||||||
|
<option value="10YCS-CG-TSO---S">ME Montenegro</option>
|
||||||
|
<option value="10YCS-SERBIATSOV">RS Serbia</option>
|
||||||
|
<option value="10YCY-1001A0003J">CY Cyprus</option>
|
||||||
|
<option value="10YCZ-CEPS-----N">CZ Czech Republic</option>
|
||||||
|
<option value="10YDK-1--------W">DK1 Denmark DK1</option>
|
||||||
|
<option value="10YDK-2--------M">DK2 Denmark DK2</option>
|
||||||
|
<option value="10YES-REE------0">ES Spain</option>
|
||||||
|
<option value="10YFI-1--------U">FI Finland</option>
|
||||||
|
<option value="10YFR-RTE------C">FR France</option>
|
||||||
|
<option value="10YGB----------A">GB Great Britain</option>
|
||||||
|
<option value="10YGR-HTSO-----Y">GR Greece</option>
|
||||||
|
<option value="10YHR-HEP------M">HR Croatia</option>
|
||||||
|
<option value="10YHU-MAVIR----U">HU Hungary</option>
|
||||||
|
<option value="10YLT-1001A0008Q">LT Lithuania</option>
|
||||||
|
<option value="10YLV-1001A00074">LV Latvia</option>
|
||||||
|
<option value="10YMK-MEPSO----8">MK FYROM</option>
|
||||||
|
<option value="10YNL----------L">NL Netherlands</option>
|
||||||
|
<option value="10YNO-1--------2">NO1 Norwegian Area Elspot Area 1</option>
|
||||||
|
<option value="10YNO-2--------T">NO2 Norwegian Area Elspot Area 2</option>
|
||||||
|
<option value="10YNO-3--------J">NO3 Norwegian Area Elspot Area 3</option>
|
||||||
|
<option value="10YNO-4--------9">NO4 Norwegian Area Elspot Area 4</option>
|
||||||
|
<option value="10YPL-AREA-----S">PL Poland</option>
|
||||||
|
<option value="10YPT-REN------W">PT Portugal</option>
|
||||||
|
<option value="10YRO-TEL------P">RO Romania</option>
|
||||||
|
<option value="10YSI-ELES-----O">SI Slovenia</option>
|
||||||
|
<option value="10YSK-SEPS-----K">SK Slovak Republic</option>
|
||||||
|
<option value="10YTR-TEIAS----W">TEIAS_AREA TEIAS Area</option>
|
||||||
|
<option value="10YUA-WEPS-----0">UA-BEI Ukrainian Area of Burshtyn island</option>
|
||||||
|
<option value="14Y----0000041-W">AT-EXAAMC Virtual Bidding Zone EXAA</option>
|
||||||
|
<option value="14YCCPADATENMLDW">CCPACCP1 CCP Austria GmbH</option>
|
||||||
|
<option value="14YEXAADATENMLDU">EXAACCP EXAA Abwicklungsstelle für Energieprodukte AG</option>
|
||||||
|
<option value="17Y000000930808E">FR_UK_IFA2000_1 vhub_UK_IFA2000_link1</option>
|
||||||
|
<option value="17Y000000930809C">FR_FR_IFA2000_1 vhub_FR_IFA2000_link1</option>
|
||||||
|
<option value="17Y000000930810R">FR_UK_IFA2000_2 vhub_UK_IFA2000_link2</option>
|
||||||
|
<option value="17Y000000930811P">FR_FR_IFA2000_2 vhub_FR_IFA2000_link2</option>
|
||||||
|
<option value="17Y000000930814J">FR_UK_IFA2 vhub_UK_IFA2</option>
|
||||||
|
<option value="17Y000000930815H">FR_FR_IFA2 vhub_FR_IFA2</option>
|
||||||
|
<option value="17Y000000930816F">FR_IT_SAV_PIE vhub_IT_Savoie_Piemont</option>
|
||||||
|
<option value="17Y000000930817D">FR_FR_SAV_PIE vhub_FR_Savoie_Piemont</option>
|
||||||
|
<option value="17Y0000009369493">FR_UK_IFA2_V_BZN IFA2_virtual_BZN</option>
|
||||||
|
<option value="44Y-00000000160K">FI_FS Fingrid Oyj</option>
|
||||||
|
<option value="44Y-00000000161I">FI_EL Fingrid Oyj</option>
|
||||||
|
<option value="45Y000000000001C">DKW-NO2 DKW-NO2 virtual Bidding Zone Border</option>
|
||||||
|
<option value="45Y000000000002A">DKW-SE3 DKW-SE3 virtual Bidding Zone Border</option>
|
||||||
|
<option value="45Y0000000000038">DKW-DKE DKW-DKE virtual Bidding Zone Border</option>
|
||||||
|
<option value="46Y000000000001Y">SE3_FS SE3_FS (SE3, Fennoskan)</option>
|
||||||
|
<option value="46Y000000000002W">SE3_KS SE3_KS (SE3, Kontiskan)</option>
|
||||||
|
<option value="46Y000000000003U">SE4_SP SE4_SP (SE4, Swepol link)</option>
|
||||||
|
<option value="46Y000000000004S">SE4_NB SE4_NB (SE4, Nordbalt)</option>
|
||||||
|
<option value="46Y000000000005Q">SE4_BC SE4_BC (SE4, Baltic Cable)</option>
|
||||||
|
<option value="46Y000000000007M">CUT_AREA_SE3LS Cut area SE3LS</option>
|
||||||
|
<option value="46Y000000000008K">CUT_AREA_SE3 Cut area SE3</option>
|
||||||
|
<option value="46Y000000000009I">CUTCOR_SE3LS-SE3 Cut corridor SE3LS-SE3</option>
|
||||||
|
<option value="46Y000000000015N">VBZ_SE3-SE4_ACSW Virtual bidding zone border SE3-SE4 AC+SWL</option>
|
||||||
|
<option value="46Y000000000016L">VBZ_SE4-SE3_ACSW Virtual bidding zone border SE4-SE3 AC+SWL</option>
|
||||||
|
<option value="46Y000000000017J">SE3_SWL SE3_SWL (SE3, Sydvastlanken)</option>
|
||||||
|
<option value="46Y000000000018H">SE4_SWL SE4_SWL (SE4, Sydvastlanken)</option>
|
||||||
|
<option value="46Y000000000019F">CUTCOR_SE3A-SE3 Cut corridor SE3A-SE3</option>
|
||||||
|
<option value="50Y0JVU59B4JWQCU">NO_NO2NSL Virtual Bidding Zone NO2NSL</option>
|
||||||
|
<option value="50Y73EMZ34CQL9AJ">NO_NO2_NL Virtual Bidding Zone NO2-NL</option>
|
||||||
|
<option value="50YCUY85S1HH29EK">NO_NO2_DK1 Virtual Bidding Zone NO2-DK1</option>
|
||||||
|
<option value="50Y-HTS3792HUOAC">NO_NO2-GB Virtual bidding Zone NO2-GB</option>
|
||||||
|
<option value="50YNBFFTWZRAHA3P">NO_NO2-DE Virtual bidding Zone NO2-DE</option>
|
||||||
|
<option value="67Y-VISKOL-2003T">VISKOL2003 VISKOL DOO NOVI SAD SRBIJA</option>
|
||||||
|
</options>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="historicDays" type="integer" min="1" max="365" required="false">
|
||||||
|
<label>Historic Data</label>
|
||||||
|
<default>1</default>
|
||||||
|
<description>Specify number of days to download historic data of energy spot prices (historic data will get exchange
|
||||||
|
rate of today)</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="resolution" type="text" required="false">
|
||||||
|
<label>Resolution</label>
|
||||||
|
<default>PT60M</default>
|
||||||
|
<description>Data resolution</description>
|
||||||
|
<limitToOptions>true</limitToOptions>
|
||||||
|
<options>
|
||||||
|
<option value="PT15M">15 minutes</option>
|
||||||
|
<option value="PT30M">30 minutes</option>
|
||||||
|
<option value="PT60M">60 minutes</option>
|
||||||
|
</options>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="spotPricesAvailableCetHour" type="integer" min="0" max="23" required="false">
|
||||||
|
<label>Publish Hour</label>
|
||||||
|
<default>13</default>
|
||||||
|
<description>CET hour spot prices will get published on ENTSO-E. Usually at 13 CET.</description>
|
||||||
|
<advanced>true</advanced>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="requestTimeout" type="integer" min="10" max="300" required="false">
|
||||||
|
<label>Request Timeout</label>
|
||||||
|
<default>30</default>
|
||||||
|
<description>Request timeout value in seconds</description>
|
||||||
|
<advanced>true</advanced>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</thing-type>
|
||||||
|
|
||||||
|
<channel-type id="spot-price">
|
||||||
|
<item-type>Number:EnergyPrice</item-type>
|
||||||
|
<label>Spot Price</label>
|
||||||
|
<category>Price</category>
|
||||||
|
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="trigger-prices-received">
|
||||||
|
<kind>trigger</kind>
|
||||||
|
<label>Trigger Prices Received</label>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
</thing:thing-descriptions>
|
@ -138,6 +138,7 @@
|
|||||||
<module>org.openhab.binding.enigma2</module>
|
<module>org.openhab.binding.enigma2</module>
|
||||||
<module>org.openhab.binding.enocean</module>
|
<module>org.openhab.binding.enocean</module>
|
||||||
<module>org.openhab.binding.enphase</module>
|
<module>org.openhab.binding.enphase</module>
|
||||||
|
<module>org.openhab.binding.entsoe</module>
|
||||||
<module>org.openhab.binding.enturno</module>
|
<module>org.openhab.binding.enturno</module>
|
||||||
<module>org.openhab.binding.ephemeris</module>
|
<module>org.openhab.binding.ephemeris</module>
|
||||||
<module>org.openhab.binding.epsonprojector</module>
|
<module>org.openhab.binding.epsonprojector</module>
|
||||||
|
Loading…
Reference in New Issue
Block a user