Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feature/create-awatta…
Browse files Browse the repository at this point in the history
…r-api-class

Signed-off-by: Thomas Leber <thomas@tl-photography.at>
  • Loading branch information
tl-photography committed Aug 9, 2024
2 parents 8935d68 + 37cd423 commit e9143b3
Show file tree
Hide file tree
Showing 185 changed files with 14,176 additions and 778 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@
/bundles/org.openhab.binding.solaredge/ @alexf2015
/bundles/org.openhab.binding.solarforecast/ @weymann
/bundles/org.openhab.binding.solarlog/ @johannrichard
/bundles/org.openhab.binding.solarman/ @catalinsanda
/bundles/org.openhab.binding.solarmax/ @jamietownsend
/bundles/org.openhab.binding.solarwatt/ @sven-carstens
/bundles/org.openhab.binding.solax/ @theater
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,11 @@
<artifactId>org.openhab.binding.solarlog</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.solarman</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.solarmax</artifactId>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions bundles/org.openhab.binding.awattar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ Also, due to the time the aWATTar API delivers the data for the next day, it doe

## Channels

### Bridge

The bridge has two channels which support a time-series:

| channel | type | description |
| ------------ |--------------------| --------------------------------------------------------------------------------------------------------------------------------------- |
| market-net | Number:EnergyPrice | This net market price per kWh. This is directly taken from the price the aWATTar API delivers. |
| total-net | Number:EnergyPrice | Sum of net market price and configured base price |

If you need gross prices, please use the [VAT profile](https://www.openhab.org/addons/transformations/vat/).

### Prices Thing

For every hour, the `prices` thing provides the following prices:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public SortedSet<AwattarPrice> getData() throws AwattarApiException {
// the whole next day.
zdt = zdt.plusDays(3);
long end = zdt.toInstant().toEpochMilli();

StringBuilder request = new StringBuilder(url);
request.append("?start=").append(start).append("&end=").append(end);

Expand All @@ -134,16 +135,18 @@ public SortedSet<AwattarPrice> getData() throws AwattarApiException {
SortedSet<AwattarPrice> result = new TreeSet<>(Comparator.comparing(AwattarPrice::timerange));

AwattarApiData apiData = gson.fromJson(content, AwattarApiData.class);

for (Datum d : apiData.data) {
// the API returns prices in €/MWh, we need €ct/kWh -> divide by 10 (100/1000)
double netPrice = d.marketprice / 10.0;
TimeRange timeRange = new TimeRange(d.startTimestamp, d.endTimestamp);

logger.trace("Adding price: netPrice = {}, timeRange = '{}'", netPrice, timeRange);
result.add(new AwattarPrice(netPrice, netPrice * vatFactor, netPrice + basePrice,
(netPrice + basePrice) * vatFactor, timeRange));
double netMarket = d.marketprice / 10.0;
double grossMarket = netMarket * vatFactor;
double netTotal = netMarket + basePrice;
double grossTotal = netTotal * vatFactor;

result.add(new AwattarPrice(netMarket, grossMarket, netTotal, grossTotal,
new TimeRange(d.startTimestamp, d.endTimestamp)));
}

return result;
} else {
throw new AwattarApiException("@text/warn.awattar.statuscode" + httpStatus);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@
*/
package org.openhab.binding.awattar.internal.handler;

import static org.openhab.binding.awattar.internal.AwattarBindingConstants.BINDING_ID;
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_MARKET_NET;
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_TOTAL_NET;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.SortedSet;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import javax.measure.Unit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
Expand All @@ -29,13 +33,17 @@
import org.openhab.binding.awattar.internal.api.AwattarApi;
import org.openhab.binding.awattar.internal.api.AwattarApi.AwattarApiException;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.CurrencyUnits;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.TimeSeries;
import org.openhab.core.types.util.UnitUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -114,21 +122,64 @@ private void refresh() {
// Method is private and only called when dataRefresher is initialized.
// DataRefresher is initialized after successful creation of AwattarApi.
prices = awattarApi.getData();

TimeSeries netMarketSeries = new TimeSeries(TimeSeries.Policy.REPLACE);
TimeSeries netTotalSeries = new TimeSeries(TimeSeries.Policy.REPLACE);

Unit<?> priceUnit = getPriceUnit();

for (AwattarPrice price : prices) {
Instant timestamp = Instant.ofEpochMilli(price.timerange().start());

netMarketSeries.add(timestamp, new QuantityType<>(price.netPrice() / 100.0, priceUnit));
netTotalSeries.add(timestamp, new QuantityType<>(price.netTotal() / 100.0, priceUnit));
}

// update channels
sendTimeSeries(CHANNEL_MARKET_NET, netMarketSeries);
sendTimeSeries(CHANNEL_TOTAL_NET, netTotalSeries);

updateStatus(ThingStatus.ONLINE);
} catch (AwattarApiException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}

private Unit<?> getPriceUnit() {
Unit<?> priceUnit = UnitUtils.parseUnit("EUR/kWh");
if (priceUnit == null) {
priceUnit = CurrencyUnits.BASE_ENERGY_PRICE;
logger.info("Using {} instead of EUR/kWh, because it is not available", priceUnit);
}
return priceUnit;
}

private void createAndSendTimeSeries(String channelId, Function<AwattarPrice, Double> valueFunction) {
SortedSet<AwattarPrice> locPrices = getPrices();
Unit<?> priceUnit = getPriceUnit();
if (locPrices == null) {
return;
}
TimeSeries timeSeries = new TimeSeries(TimeSeries.Policy.REPLACE);
locPrices.forEach(p -> {
timeSeries.add(Instant.ofEpochMilli(p.timerange().start()),
new QuantityType<>(valueFunction.apply(p) / 100.0, priceUnit));
});
sendTimeSeries(channelId, timeSeries);
}

/**
* Check if the data needs to be refreshed.
*
* The data is refreshed if:
* - the thing is offline
* - the local cache is empty
* - the current time is after 15:00 and the last refresh was more than an hour ago
* - the current time is after 18:00 and the last refresh was more than an hour ago
* - the current time is after 21:00 and the last refresh was more than an hour ago
* - the current time is after 15:00 and the last refresh was more than an hour
* ago
* - the current time is after 18:00 and the last refresh was more than an hour
* ago
* - the current time is after 21:00 and the last refresh was more than an hour
* ago
*
* @return true if the data needs to be refreshed
*/
Expand Down Expand Up @@ -196,9 +247,10 @@ public boolean containsPriceFor(long timestamp) {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
refresh();
} else {
logger.debug("Binding {} only supports refresh command", BINDING_ID);
switch (channelUID.getId()) {
case CHANNEL_MARKET_NET -> createAndSendTimeSeries(CHANNEL_MARKET_NET, AwattarPrice::netPrice);
case CHANNEL_TOTAL_NET -> createAndSendTimeSeries(CHANNEL_TOTAL_NET, AwattarPrice::netTotal);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
<option value="AT">AT</option>
</options>
</parameter>
<parameter name="vatPercent" type="decimal">
<parameter name="vatPercent" type="decimal" min="0">
<label>VAT Percent</label>
<description>Specifies the value added tax percentage</description>
<default>19</default>
</parameter>
<parameter name="basePrice" type="decimal">
<parameter name="basePrice" type="decimal" min="0" step="0.001">
<label>Base Price</label>
<description>Specifies the net base price per kWh</description>
<default>0</default>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@
<bridge-type id="bridge">
<label>aWATTar Bridge</label>
<description>Provides price data from the aWATTar API.</description>

<channels>
<channel id="market-net" typeId="uom-price">
<label>Net Market Price</label>
<description>Price without VAT and network charge</description>
</channel>
<channel id="market-gross" typeId="uom-price">
<label>Gross Market Price</label>
<description>Price with VAT but without network charge</description>
</channel>
<channel id="total-net" typeId="uom-price">
<label>Net Total Price</label>
<description>Price with network charge but without VAT</description>
</channel>
<channel id="total-gross" typeId="uom-price">
<label>Gross Total Price</label>
<description>Price with network charge and VAT</description>
</channel>
</channels>

<properties>
<property name="thingTypeVersion">1</property>
</properties>

<config-description-ref uri="bridge-type:awattar:bridge"/>
</bridge-type>

Expand Down Expand Up @@ -250,6 +274,12 @@
<config-description-ref uri="thing-type:awattar:bestprice"/>
</thing-type>

<channel-type id="uom-price">
<item-type>Number:EnergyPrice</item-type>
<label>Price</label>
<state readOnly="true" pattern="%.3f %unit%"/>
</channel-type>

<channel-type id="price">
<item-type>Number</item-type>
<label>Price</label>
Expand Down
Loading

0 comments on commit e9143b3

Please sign in to comment.