Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ENTSO-E, Time-of-Use Tariff provider #2207

Merged
merged 20 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions io.openems.common/src/io/openems/common/utils/XmlUtils.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package io.openems.common.utils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.w3c.dom.DOMException;
import org.w3c.dom.NamedNodeMap;
Expand Down Expand Up @@ -207,4 +211,47 @@ public static String getContentAsString(Node node) {
public static int getContentAsInt(Node node) {
return Integer.parseInt(node.getTextContent());
}

/**
* Iterates through a {@link Node}.
*
* <p>
* Source: https://stackoverflow.com/a/48153597/4137113
*
* @param node the {@link Node}
* @return the {@link Iterable}
*/
public static Iterable<Node> list(final Node node) {
return () -> new Iterator<Node>() {

private int index = 0;

@Override
public boolean hasNext() {
return this.index < node.getChildNodes().getLength();
}

@Override
public Node next() {
if (!this.hasNext()) {
throw new NoSuchElementException();
}
return node.getChildNodes().item(this.index++);
}
};
}

/**
* Iterates over {@link Node} through {@link Stream}.
*
* <p>
* Source: https://stackoverflow.com/a/62171621/4137113
*
* @param node the {@link Node}
* @return the {@link Stream}
*/
public static Stream<Node> stream(final Node node) {
var childNodes = node.getChildNodes();
return IntStream.range(0, childNodes.getLength()).boxed().map(childNodes::item);
}
}
2 changes: 2 additions & 0 deletions io.openems.edge.application/EdgeApp.bndrun
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
bnd.identity;id='io.openems.edge.timedata.rrd4j',\
bnd.identity;id='io.openems.edge.timeofusetariff.awattar',\
bnd.identity;id='io.openems.edge.timeofusetariff.corrently',\
bnd.identity;id='io.openems.edge.timeofusetariff.entsoe',\
venu-sagar marked this conversation as resolved.
Show resolved Hide resolved
bnd.identity;id='io.openems.edge.timeofusetariff.tibber',\

-runbundles: \
Expand Down Expand Up @@ -342,6 +343,7 @@
io.openems.edge.timeofusetariff.api;version=snapshot,\
io.openems.edge.timeofusetariff.awattar;version=snapshot,\
io.openems.edge.timeofusetariff.corrently;version=snapshot,\
io.openems.edge.timeofusetariff.entsoe;version=snapshot,\
io.openems.edge.timeofusetariff.tibber;version=snapshot,\
io.openems.shared.influxdb;version=snapshot,\
io.openems.wrapper.eu.chargetime.ocpp;version=snapshot,\
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.openems.edge.common.currency;

import io.openems.common.types.OptionsEnum;

public enum Currency implements OptionsEnum {
UNDEFINED(-1, "-"), //
EUR(0, "€"), //
SEK(1, "kr"), //
;

private final String name;
private final int value;

private Currency(int value, String name) {
this.value = value;
this.name = name;
}

@Override
public int getValue() {
return this.value;
}

@Override
public String getName() {
return this.name;
}

@Override
public OptionsEnum getUndefined() {
return Currency.UNDEFINED;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.openems.edge.common.currency;

import io.openems.edge.common.meta.Meta;
import io.openems.edge.common.meta.Meta.ChannelId;

/**
* The {@link ChannelId#CURRENCY} mandates the selection of the 'currency'
* configuration property of this specific type. Subsequently, this selected
* property is transformed into the corresponding {@link Currency} type before
* being written through {@link Meta#_setCurrency(Currency)}.
*/
public enum CurrencyConfig {
/**
* Euro.
*/
EUR,
/**
* Swedish Krona.
*/
SEK;

/**
* Converts the {@link CurrencyConfig} to the {@link Currency}.
*
* @return The {@link Currency}.
*/
public Currency toCurrency() {
return switch (this) {
case EUR -> Currency.EUR;
case SEK -> Currency.SEK;
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@org.osgi.annotation.versioning.Version("1.0.0")
@org.osgi.annotation.bundle.Export
package io.openems.edge.common.currency;
41 changes: 40 additions & 1 deletion io.openems.edge.common/src/io/openems/edge/common/meta/Meta.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import io.openems.common.channel.AccessMode;
import io.openems.common.types.OpenemsType;
import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.channel.EnumReadChannel;
import io.openems.edge.common.channel.value.Value;
import io.openems.edge.common.currency.Currency;
import io.openems.edge.common.modbusslave.ModbusSlave;
import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable;
import io.openems.edge.common.modbusslave.ModbusSlaveTable;
Expand All @@ -22,7 +25,17 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
* <li>Type: String
* </ul>
*/
VERSION(Doc.of(OpenemsType.STRING));
VERSION(Doc.of(OpenemsType.STRING)),

/**
* Edge currency.
*
* <ul>
* <li>Interface: Meta
* <li>Type: Currency
* </ul>
*/
CURRENCY(Doc.of(Currency.values()));

private final Doc doc;

Expand Down Expand Up @@ -52,4 +65,30 @@ public default ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
.build());
}

/**
* Gets the Channel for {@link ChannelId#CURRENCY}.
*
* @return the Channel
*/
public default EnumReadChannel getCurrencyChannel() {
return this.channel(ChannelId.CURRENCY);
}

/**
* Gets the Capacity in [Wh]. See {@link ChannelId#CURRENCY}.
*
* @return the Channel {@link Value}
*/
public default Currency getCurrency() {
return this.getCurrencyChannel().value().asEnum();
}

/**
* Internal method to set the 'nextValue' on {@link ChannelId#CURRENCY} Channel.
*
* @param value the next value
*/
public default void _setCurrency(Currency value) {
this.getCurrencyChannel().setNextValue(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.openems.edge.common.test;

import io.openems.edge.common.channel.Channel;
import io.openems.edge.common.component.AbstractOpenemsComponent;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.currency.Currency;
import io.openems.edge.common.meta.Meta;

public class DummyMeta extends AbstractOpenemsComponent implements Meta {

public DummyMeta(String id, Currency currency) {
super(//
OpenemsComponent.ChannelId.values(), //
Meta.ChannelId.values() //
);
for (Channel<?> channel : this.channels()) {
channel.nextProcessImage();
}
this._setCurrency(currency);
super.activate(null, id, "", true);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package io.openems.edge.core.meta;

import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;

import io.openems.edge.common.currency.CurrencyConfig;

@ObjectClassDefinition(//
name = "Core Meta", //
description = "The global manager for Metadata.")
@interface Config {

String webconsole_configurationFactory_nameHint() default "Core Meta";

@AttributeDefinition(name = "Currency", description = "Every monetary value is inherently expressed in this Currency. Values obtained in a different currency (e.g. energy prices from a web service) are internally converted to this Currency using the current exchange rate.")
CurrencyConfig currency() default CurrencyConfig.EUR;

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,20 @@ public MetaImpl() {
}

@Activate
private void activate(ComponentContext context) {
private void activate(ComponentContext context, Config config) {
super.activate(context, SINGLETON_COMPONENT_ID, Meta.SINGLETON_SERVICE_PID, true);

this.applyConfig(config);
venu-sagar marked this conversation as resolved.
Show resolved Hide resolved
if (OpenemsComponent.validateSingleton(this.cm, Meta.SINGLETON_SERVICE_PID, SINGLETON_COMPONENT_ID)) {
return;
}
}

@Modified
private void modified(ComponentContext context) {
private void modified(ComponentContext context, Config config) {
super.modified(context, SINGLETON_COMPONENT_ID, Meta.SINGLETON_SERVICE_PID, true);

this.applyConfig(config);
if (OpenemsComponent.validateSingleton(this.cm, Meta.SINGLETON_SERVICE_PID, SINGLETON_COMPONENT_ID)) {
return;
}
Expand All @@ -59,4 +61,7 @@ protected void deactivate() {
super.deactivate();
}

private void applyConfig(Config config) {
this._setCurrency(config.currency().toCurrency());
}
}
12 changes: 12 additions & 0 deletions io.openems.edge.timeofusetariff.entsoe/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
<classpathentry kind="src" output="bin" path="src"/>
<classpathentry kind="src" output="bin_test" path="test">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
2 changes: 2 additions & 0 deletions io.openems.edge.timeofusetariff.entsoe/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin_test/
/generated/
23 changes: 23 additions & 0 deletions io.openems.edge.timeofusetariff.entsoe/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>io.openems.edge.timeofusetariff.entsoe</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>bndtools.core.bndbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>bndtools.core.bndnature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
venu-sagar marked this conversation as resolved.
Show resolved Hide resolved
encoding/<project>=UTF-8
16 changes: 16 additions & 0 deletions io.openems.edge.timeofusetariff.entsoe/bnd.bnd
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Bundle-Name: OpenEMS Edge Time-Of-Use ENTSO-E
Bundle-Vendor: FENECON GmbH
Bundle-License: https://opensource.org/licenses/EPL-2.0
Bundle-Version: 1.0.0.${tstamp}

-buildpath: \
${buildpath},\
com.squareup.okio,\
io.openems.common,\
io.openems.edge.common,\
io.openems.edge.timeofusetariff.api,\
io.openems.wrapper.okhttp,\

-testpath: \
org.jetbrains.kotlin.osgi-bundle,\
${testpath}
11 changes: 11 additions & 0 deletions io.openems.edge.timeofusetariff.entsoe/readme.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
= Time-Of-Use Tariff ENTSO-E

This implementation uses the ENTSO-E transparency platform to receive day-ahead prices in European power grids.

To request a (free) authentication token, please see chapter "2. Authentication and Authorisation" in the official API documentation: https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html#_authentication_and_authorisation

Prices retrieved from ENTSO-E are subsequently converted to the user's currency (defined in Core.Meta Component) using the Exchange Rates API.

For detailed information about the Exchange Rates API, please refer to: https://exchangerate.host/#/docs

https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.timeofusetariff.entsoe[Source Code icon:github[]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.openems.edge.timeofusetariff.entsoe;

/**
* https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html#_areas.
*/
public enum BiddingZone {
/**
* BZN|DE-LU.
*/
GERMANY("10Y1001A1001A82H"), //
/**
* BZN|AT.
*/
AUSTRIA("10YAT-APG------L"), //
/**
* BZN|SE1.
*/
SWEDEN_SE1("10Y1001A1001A44P"), //
/**
* BZN|SE2.
*/
SWEDEN_SE2("10Y1001A1001A45N"), //
/**
* BZN|SE3.
*/
SWEDEN_SE3("10Y1001A1001A46L"), //
/**
* BZN|SE4.
*/
SWEDEN_SE4("10Y1001A1001A47J"), //
;

public final String code;

private BiddingZone(String code) {
this.code = code;
}
}
Loading