From: J-N-K Date: Tue, 6 Aug 2024 22:00:13 +0000 (+0200) Subject: [awattar] Add TimeSeries support (#17172) X-Git-Url: https://git.basschouten.com/?a=commitdiff_plain;h=81251c3b35899205f04c64da1a0e6f8b8af73829;p=openhab-addons.git [awattar] Add TimeSeries support (#17172) Signed-off-by: Jan N. Klug --- diff --git a/bundles/org.openhab.binding.awattar/README.md b/bundles/org.openhab.binding.awattar/README.md index e0c227f45f..f68bbd6c6f 100644 --- a/bundles/org.openhab.binding.awattar/README.md +++ b/bundles/org.openhab.binding.awattar/README.md @@ -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: diff --git a/bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandler.java b/bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandler.java index 063148bec6..f33ce3a34d 100644 --- a/bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandler.java +++ b/bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandler.java @@ -14,7 +14,7 @@ package org.openhab.binding.awattar.internal.handler; import static org.eclipse.jetty.http.HttpMethod.GET; import static org.eclipse.jetty.http.HttpStatus.OK_200; -import static org.openhab.binding.awattar.internal.AwattarBindingConstants.BINDING_ID; +import static org.openhab.binding.awattar.internal.AwattarBindingConstants.*; import java.time.Instant; import java.time.LocalDate; @@ -27,6 +27,9 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Function; + +import javax.measure.Unit; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -37,6 +40,8 @@ import org.openhab.binding.awattar.internal.AwattarPrice; import org.openhab.binding.awattar.internal.dto.AwattarApiData; import org.openhab.binding.awattar.internal.dto.Datum; 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; @@ -44,6 +49,8 @@ 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; @@ -152,13 +159,30 @@ public class AwattarBridgeHandler extends BaseBridgeHandler { SortedSet result = new TreeSet<>(Comparator.comparing(AwattarPrice::timerange)); AwattarApiData apiData = gson.fromJson(content, AwattarApiData.class); if (apiData != null) { + TimeSeries netMarketSeries = new TimeSeries(TimeSeries.Policy.REPLACE); + TimeSeries netTotalSeries = new TimeSeries(TimeSeries.Policy.REPLACE); + + Unit priceUnit = getPriceUnit(); + for (Datum d : apiData.data) { - double netPrice = d.marketprice / 10.0; - TimeRange timerange = new TimeRange(d.startTimestamp, d.endTimestamp); - 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; + Instant timestamp = Instant.ofEpochMilli(d.startTimestamp); + + netMarketSeries.add(timestamp, new QuantityType<>(netMarket / 100.0, priceUnit)); + netTotalSeries.add(timestamp, new QuantityType<>(netTotal / 100.0, priceUnit)); + + result.add(new AwattarPrice(netMarket, grossMarket, netTotal, grossTotal, + new TimeRange(d.startTimestamp, d.endTimestamp))); } prices = result; + + // update channels + sendTimeSeries(CHANNEL_MARKET_NET, netMarketSeries); + sendTimeSeries(CHANNEL_TOTAL_NET, netTotalSeries); + updateStatus(ThingStatus.ONLINE); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, @@ -179,6 +203,29 @@ public class AwattarBridgeHandler extends BaseBridgeHandler { } } + 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 valueFunction) { + SortedSet prices = getPrices(); + Unit priceUnit = getPriceUnit(); + if (prices == null) { + return; + } + TimeSeries timeSeries = new TimeSeries(TimeSeries.Policy.REPLACE); + prices.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. * @@ -253,9 +300,10 @@ public class AwattarBridgeHandler extends BaseBridgeHandler { @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); + } } } } diff --git a/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/config/config.xml index 10445470f9..5fa754579e 100644 --- a/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/config/config.xml @@ -14,12 +14,12 @@ - + Specifies the value added tax percentage 19 - + Specifies the net base price per kWh 0 diff --git a/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/thing/thing-types.xml index 837435424b..47fcb5a129 100644 --- a/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/thing/thing-types.xml @@ -7,6 +7,30 @@ Provides price data from the aWATTar API. + + + + + Price without VAT and network charge + + + + Price with VAT but without network charge + + + + Price with network charge but without VAT + + + + Price with network charge and VAT + + + + + 1 + + @@ -250,6 +274,12 @@ + + Number:EnergyPrice + + + + Number diff --git a/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/update/update.xml b/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/update/update.xml new file mode 100644 index 0000000000..5ab170fdd2 --- /dev/null +++ b/bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/update/update.xml @@ -0,0 +1,21 @@ + + + + + + + awattar:uom-price + + Price without VAT and network charge + + + awattar:uom-price + + Price with network charge but without VAT + + + + + diff --git a/bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerRefreshTest.java b/bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerRefreshTest.java index afdf8892e9..7cb1ccc46d 100644 --- a/bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerRefreshTest.java +++ b/bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerRefreshTest.java @@ -92,6 +92,7 @@ public class AwattarBridgeHandlerRefreshTest extends JavaTest { when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.of("GMT+2")); + when(bridgeMock.getUID()).thenReturn(BRIDGE_UID); bridgeHandler = new AwattarBridgeHandler(bridgeMock, httpClientMock, timeZoneProviderMock); bridgeHandler.setCallback(bridgeCallbackMock); diff --git a/bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerTest.java b/bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerTest.java index b4d5ace5b8..b90d3f1ed0 100644 --- a/bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerTest.java +++ b/bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerTest.java @@ -105,10 +105,10 @@ public class AwattarBridgeHandlerTest extends JavaTest { when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.of("GMT+2")); + when(bridgeMock.getUID()).thenReturn(BRIDGE_UID); bridgeHandler = new AwattarBridgeHandler(bridgeMock, httpClientMock, timeZoneProviderMock); bridgeHandler.setCallback(bridgeCallbackMock); bridgeHandler.refreshIfNeeded(); - when(bridgeMock.getHandler()).thenReturn(bridgeHandler); // other mocks