diff --git a/CODEOWNERS b/CODEOWNERS
index f2eb395a8a3..9da5cbb79ee 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -104,6 +104,7 @@
/bundles/org.openhab.binding.enigma2/ @gdolfen
/bundles/org.openhab.binding.enocean/ @fruggy83
/bundles/org.openhab.binding.enphase/ @Hilbrand
+/bundles/org.openhab.binding.entsoe/ @jmelhus
/bundles/org.openhab.binding.enturno/ @klocsson
/bundles/org.openhab.binding.ephemeris/ @clinique
/bundles/org.openhab.binding.epsonprojector/ @mlobstein
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 0604b3a3956..3bd18dbc0ba 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -511,6 +511,11 @@
org.openhab.binding.enphase
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.entsoe
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.enturno
diff --git a/bundles/org.openhab.binding.entsoe/NOTICE b/bundles/org.openhab.binding.entsoe/NOTICE
new file mode 100644
index 00000000000..38d625e3492
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/NOTICE
@@ -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
diff --git a/bundles/org.openhab.binding.entsoe/README.md b/bundles/org.openhab.binding.entsoe/README.md
new file mode 100644
index 00000000000..e3ba543abe1
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/README.md
@@ -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" { 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
+```
diff --git a/bundles/org.openhab.binding.entsoe/pom.xml b/bundles/org.openhab.binding.entsoe/pom.xml
new file mode 100644
index 00000000000..baf37c2bf9b
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 4.3.0-SNAPSHOT
+
+
+ org.openhab.binding.entsoe
+
+ openHAB Add-ons :: Bundles :: ENTSO-E Binding
+
+
diff --git a/bundles/org.openhab.binding.entsoe/src/main/feature/feature.xml b/bundles/org.openhab.binding.entsoe/src/main/feature/feature.xml
new file mode 100644
index 00000000000..701a921c9a3
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/feature/feature.xml
@@ -0,0 +1,9 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ mvn:org.openhab.addons.bundles/org.openhab.binding.entsoe/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeBindingConstants.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeBindingConstants.java
new file mode 100644
index 00000000000..9356668e3f1
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeBindingConstants.java
@@ -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 SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_DAY_AHEAD);
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeConfiguration.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeConfiguration.java
new file mode 100644
index 00000000000..3b5cd45afc4
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeConfiguration.java
@@ -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";
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeHandler.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeHandler.java
new file mode 100644
index 00000000000..78ba5c27068
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeHandler.java
@@ -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 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 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 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 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());
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeHandlerFactory.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeHandlerFactory.java
new file mode 100644
index 00000000000..8fa2febdaeb
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/EntsoeHandlerFactory.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/Client.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/Client.java
new file mode 100644
index 00000000000..4384d69ff5c
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/Client.java
@@ -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 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 parseXmlResponse(String responseText, String configResolution)
+ throws ParserConfigurationException, SAXException, IOException, EntsoeResponseException,
+ EntsoeConfigurationException {
+ Map 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/Request.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/Request.java
new file mode 100644
index 00000000000..fe9c73f038c
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/Request.java
@@ -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();
+ }
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/SpotPrice.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/SpotPrice.java
new file mode 100644
index 00000000000..24159da7ede
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/client/SpotPrice.java
@@ -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 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 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 toUnit) {
+ return new DecimalType(toUnit.getConverterTo(this.unit).convert(this.price)).toBigDecimal();
+ }
+
+ public State getState(Unit 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeConfigurationException.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeConfigurationException.java
new file mode 100644
index 00000000000..54cdf5b9195
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeConfigurationException.java
@@ -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);
+ }
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeResponseException.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeResponseException.java
new file mode 100644
index 00000000000..39f79372453
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeResponseException.java
@@ -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);
+ }
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeResponseMapException.java b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeResponseMapException.java
new file mode 100644
index 00000000000..a81fdf3b9e9
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/java/org/openhab/binding/entsoe/internal/exception/EntsoeResponseMapException.java
@@ -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);
+ }
+}
diff --git a/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644
index 00000000000..00003455d6c
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/addon/addon.xml
@@ -0,0 +1,13 @@
+
+
+
+ binding
+ ENTSO-E Binding
+ This is the binding for European Network of Transmission System Operators (ENTSO-E) to receive electricity
+ market spot prices.
+
+ cloud
+
+
diff --git a/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/i18n/entsoe.properties b/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/i18n/entsoe.properties
new file mode 100644
index 00000000000..ee4e53389a8
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/i18n/entsoe.properties
@@ -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
diff --git a/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 00000000000..6845becf62c
--- /dev/null
+++ b/bundles/org.openhab.binding.entsoe/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+ Hourly spot prices of the electricity market from European Network of Transmission System Operators
+ (ENTSO-E)
+ WebService
+
+
+
+
+
+
+
+ password
+
+ Security token for ENTSO-E API
+
+
+
+ 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)
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ Specify number of days to download historic data of energy spot prices (historic data will get exchange
+ rate of today)
+
+
+
+ PT60M
+ Data resolution
+ true
+
+
+
+
+
+
+
+
+ 13
+ CET hour spot prices will get published on ENTSO-E. Usually at 13 CET.
+ true
+
+
+
+ 30
+ Request timeout value in seconds
+ true
+
+
+
+
+
+ Number:EnergyPrice
+
+ Price
+
+
+
+
+ trigger
+
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index b89c5cc8ea1..b3a95a4c86b 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -138,6 +138,7 @@
org.openhab.binding.enigma2
org.openhab.binding.enocean
org.openhab.binding.enphase
+ org.openhab.binding.entsoe
org.openhab.binding.enturno
org.openhab.binding.ephemeris
org.openhab.binding.epsonprojector