-
Notifications
You must be signed in to change notification settings - Fork 397
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Implemented Tibber API to retrieve the prices for Time-Of-Use Tariff API service. Coorent readme doc for corrently. Co-authored-by: Stefan Feilmeier <[email protected]> Reviewed-on: https://git.intranet.fenecon.de/FENECON/fems/pulls/173 Reviewed-by: Stefan Feilmeier <[email protected]> Co-authored-by: Sagar Venu <[email protected]> Co-committed-by: Sagar Venu <[email protected]> (cherry picked from commit b798b26) Co-authored-by: Sagar Venu <[email protected]>
- Loading branch information
1 parent
b724fef
commit 40f58e2
Showing
13 changed files
with
539 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
= Time-Of-Use Tariff Corrently by STROMDAO | ||
|
||
Retrieves the hourly prices from the Corrently API and converts them into quarterly prices. Prices are updated every day at 14:00 and stored locally. | ||
Retrieves the quarterly prices from the Corrently API. Prices are updated every day at 14:00 and stored locally. | ||
|
||
https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.timeofusetariff.corrently[Source Code icon:github[]] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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-1.8"/> | ||
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/bin_test/ | ||
/generated/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.tibber</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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Bundle-Name: OpenEMS Edge Time-Of-Use Tariff Tibber | ||
Bundle-Vendor: FENECON GmbH | ||
Bundle-License: https://opensource.org/licenses/EPL-2.0 | ||
Bundle-Version: 1.0.0.${tstamp} | ||
|
||
-buildpath: \ | ||
${buildpath},\ | ||
io.openems.common,\ | ||
io.openems.edge.common,\ | ||
io.openems.edge.timeofusetariff.api,\ | ||
io.openems.wrapper.okhttp,\ | ||
|
||
-testpath: \ | ||
${testpath} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
= Time-Of-Use Tariff Tibber | ||
|
||
Retrieves the hourly prices from the Tibber API and converts them into quarterly prices. Prices are updated every day at 14:00 and stored locally. | ||
|
||
https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.timeofusetariff.corrently[Source Code icon:github[]] |
25 changes: 25 additions & 0 deletions
25
...penems.edge.timeofusetariff.tibber/src/io/openems/edge/timeofusetariff/tibber/Config.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package io.openems.edge.timeofusetariff.tibber; | ||
|
||
import org.osgi.service.metatype.annotations.AttributeDefinition; | ||
import org.osgi.service.metatype.annotations.AttributeType; | ||
import org.osgi.service.metatype.annotations.ObjectClassDefinition; | ||
|
||
@ObjectClassDefinition(// | ||
name = "Time-Of-Use Tariff Tibber", // | ||
description = "Time-Of-Use Tariff implementation for Tibber.") | ||
@interface Config { | ||
|
||
@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") | ||
String id() default "timeOfUseTariff0"; | ||
|
||
@AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID") | ||
String alias() default ""; | ||
|
||
@AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") | ||
boolean enabled() default true; | ||
|
||
@AttributeDefinition(name = "Access Token", description = "Access token for the Tibber API", type = AttributeType.PASSWORD) | ||
String accessToken() default ""; | ||
|
||
String webconsole_configurationFactory_nameHint() default "Time-Of-Use Tariff Tibber [{id}]"; | ||
} |
26 changes: 26 additions & 0 deletions
26
...penems.edge.timeofusetariff.tibber/src/io/openems/edge/timeofusetariff/tibber/Tibber.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package io.openems.edge.timeofusetariff.tibber; | ||
|
||
import io.openems.common.types.OpenemsType; | ||
import io.openems.edge.common.channel.Doc; | ||
import io.openems.edge.common.component.OpenemsComponent; | ||
import io.openems.edge.timeofusetariff.api.TimeOfUseTariff; | ||
|
||
public interface Tibber extends TimeOfUseTariff, OpenemsComponent { | ||
|
||
public enum ChannelId implements io.openems.edge.common.channel.ChannelId { | ||
HTTP_STATUS_CODE(Doc.of(OpenemsType.INTEGER)// | ||
.text("Displays the HTTP status code"))// | ||
; | ||
|
||
private final Doc doc; | ||
|
||
private ChannelId(Doc doc) { | ||
this.doc = doc; | ||
} | ||
|
||
@Override | ||
public Doc doc() { | ||
return this.doc; | ||
} | ||
} | ||
} |
213 changes: 213 additions & 0 deletions
213
...ms.edge.timeofusetariff.tibber/src/io/openems/edge/timeofusetariff/tibber/TibberImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
package io.openems.edge.timeofusetariff.tibber; | ||
|
||
import java.io.IOException; | ||
import java.time.Clock; | ||
import java.time.Duration; | ||
import java.time.ZoneId; | ||
import java.time.ZonedDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
import java.time.temporal.ChronoUnit; | ||
import java.util.TreeMap; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.ScheduledExecutorService; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
|
||
import org.osgi.service.component.ComponentContext; | ||
import org.osgi.service.component.annotations.Activate; | ||
import org.osgi.service.component.annotations.Component; | ||
import org.osgi.service.component.annotations.ConfigurationPolicy; | ||
import org.osgi.service.component.annotations.Deactivate; | ||
import org.osgi.service.component.annotations.Reference; | ||
import org.osgi.service.metatype.annotations.Designate; | ||
|
||
import com.google.common.collect.ImmutableSortedMap; | ||
import com.google.gson.JsonArray; | ||
import com.google.gson.JsonElement; | ||
import com.google.gson.JsonObject; | ||
|
||
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; | ||
import io.openems.common.utils.JsonUtils; | ||
import io.openems.common.utils.ThreadPoolUtils; | ||
import io.openems.edge.common.component.AbstractOpenemsComponent; | ||
import io.openems.edge.common.component.ComponentManager; | ||
import io.openems.edge.common.component.OpenemsComponent; | ||
import io.openems.edge.timeofusetariff.api.TimeOfUsePrices; | ||
import io.openems.edge.timeofusetariff.api.TimeOfUseTariff; | ||
import io.openems.edge.timeofusetariff.api.utils.TimeOfUseTariffUtils; | ||
import okhttp3.MediaType; | ||
import okhttp3.OkHttpClient; | ||
import okhttp3.Request; | ||
import okhttp3.RequestBody; | ||
import okhttp3.Response; | ||
|
||
@Designate(ocd = Config.class, factory = true) | ||
@Component(// | ||
name = "TimeOfUseTariff.Tibber", // | ||
immediate = true, // | ||
configurationPolicy = ConfigurationPolicy.REQUIRE // | ||
) | ||
public class TibberImpl extends AbstractOpenemsComponent implements TimeOfUseTariff, OpenemsComponent, Tibber { | ||
|
||
private static final String TIBBER_API_URL = "https://api.tibber.com/v1-beta/gql"; | ||
|
||
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); | ||
|
||
private Config config = null; | ||
|
||
private final AtomicReference<ImmutableSortedMap<ZonedDateTime, Float>> prices = new AtomicReference<ImmutableSortedMap<ZonedDateTime, Float>>( | ||
ImmutableSortedMap.of()); | ||
|
||
private ZonedDateTime updateTimeStamp = null; | ||
|
||
private final Runnable task = () -> { | ||
|
||
/* | ||
* Update Map of prices | ||
*/ | ||
OkHttpClient client = new OkHttpClient(); | ||
MediaType mediaType = MediaType.parse("application/json"); | ||
RequestBody body = RequestBody.create(mediaType, JsonUtils.buildJsonObject() // | ||
.addProperty("query", // | ||
"{\n" | ||
+ " viewer {\n" | ||
+ " homes {\n" | ||
+ " currentSubscription{\n" | ||
+ " priceInfo{\n" | ||
+ " today {\n" | ||
+ " total\n" | ||
+ " startsAt\n" | ||
+ " }\n" | ||
+ " tomorrow {\n" | ||
+ " total\n" | ||
+ " startsAt\n" | ||
+ " }\n" | ||
+ " }\n" | ||
+ " }\n" | ||
+ " }\n" | ||
+ " }\n" | ||
+ "}" + "") // | ||
.build().toString()); | ||
Request request = new Request.Builder() // | ||
.url(TIBBER_API_URL) // | ||
.header("Authorization", this.config.accessToken()).post(body) // | ||
.build(); | ||
int httpStatusCode; | ||
try (Response response = client.newCall(request).execute()) { | ||
httpStatusCode = response.code(); | ||
|
||
if (!response.isSuccessful()) { | ||
throw new IOException("Unexpected code " + response); | ||
} | ||
|
||
// Parse the response for the prices | ||
this.prices.set(TibberImpl.parsePrices(response.body().string())); | ||
|
||
// store the time stamp | ||
this.updateTimeStamp = ZonedDateTime.now(); | ||
|
||
} catch (IOException | OpenemsNamedException e) { | ||
e.printStackTrace(); | ||
httpStatusCode = 0; | ||
} | ||
|
||
this.channel(Tibber.ChannelId.HTTP_STATUS_CODE).setNextValue(httpStatusCode); | ||
|
||
/* | ||
* Schedule next price update for 2 pm | ||
*/ | ||
ZonedDateTime now = ZonedDateTime.now(); | ||
ZonedDateTime nextRun = now.withHour(14).truncatedTo(ChronoUnit.HOURS); | ||
if (now.isAfter(nextRun)) { | ||
nextRun = nextRun.plusDays(1); | ||
} | ||
|
||
Duration duration = Duration.between(now, nextRun); | ||
long delay = duration.getSeconds(); | ||
|
||
this.executor.schedule(this.task, delay, TimeUnit.SECONDS); | ||
}; | ||
|
||
@Reference | ||
private ComponentManager componentManager; | ||
|
||
public TibberImpl() { | ||
super(// | ||
OpenemsComponent.ChannelId.values(), // | ||
Tibber.ChannelId.values() // | ||
); | ||
} | ||
|
||
@Activate | ||
void activate(ComponentContext context, Config config) { | ||
super.activate(context, config.id(), config.alias(), config.enabled()); | ||
|
||
if (!config.enabled()) { | ||
return; | ||
} | ||
this.config = config; | ||
this.executor.schedule(this.task, 0, TimeUnit.SECONDS); | ||
} | ||
|
||
@Deactivate | ||
protected void deactivate() { | ||
super.deactivate(); | ||
ThreadPoolUtils.shutdownAndAwaitTermination(this.executor, 0); | ||
} | ||
|
||
@Override | ||
public TimeOfUsePrices getPrices() { | ||
// return null if data is not yet available. | ||
if (this.updateTimeStamp == null) { | ||
return null; | ||
} | ||
|
||
return TimeOfUseTariffUtils.getNext24HourPrices(Clock.systemDefaultZone() /* can be mocked for testing */, | ||
this.prices.get(), this.updateTimeStamp); | ||
} | ||
|
||
/** | ||
* Parse the Tibber JSON to the Price Map. | ||
* | ||
* @param jsonData the Tibber JSON | ||
* @return the Price Map | ||
* @throws OpenemsNamedException on error | ||
*/ | ||
public static ImmutableSortedMap<ZonedDateTime, Float> parsePrices(String jsonData) throws OpenemsNamedException { | ||
TreeMap<ZonedDateTime, Float> result = new TreeMap<>(); | ||
|
||
if (!jsonData.isEmpty()) { | ||
|
||
JsonObject line = JsonUtils.parseToJsonObject(jsonData); | ||
JsonArray homes = JsonUtils.getAsJsonObject(line, "data") // | ||
.getAsJsonObject("viewer") // | ||
.getAsJsonArray("homes"); | ||
|
||
for (JsonElement home : homes) { | ||
|
||
JsonObject priceInfo = JsonUtils.getAsJsonObject(home, "currentSubscription") // | ||
.getAsJsonObject("priceInfo"); | ||
|
||
// Price info for today and tomorrow. | ||
JsonArray today = JsonUtils.getAsJsonArray(priceInfo, "today"); | ||
JsonArray tomorrow = JsonUtils.getAsJsonArray(priceInfo, "tomorrow"); | ||
|
||
// Adding to an array to avoid individual variables for individual for loops. | ||
JsonArray[] days = { today, tomorrow }; | ||
|
||
// parse the arrays for price and time stamps. | ||
for (JsonArray day : days) { | ||
for (JsonElement element : day) { | ||
float marketPrice = JsonUtils.getAsFloat(element, "total"); | ||
ZonedDateTime startTime = ZonedDateTime | ||
.parse(JsonUtils.getAsString(element, "startsAt"), DateTimeFormatter.ISO_DATE_TIME) | ||
.withZoneSameInstant(ZoneId.systemDefault()); | ||
// Adding the values in the Map. | ||
result.put(startTime, marketPrice); | ||
} | ||
} | ||
} | ||
} | ||
return ImmutableSortedMap.copyOf(result); | ||
} | ||
} |
Empty file.
Oops, something went wrong.