]> git.basschouten.com Git - openhab-addons.git/commitdiff
[awattar] Add TimeSeries support (#17172)
authorJ-N-K <github@klug.nrw>
Tue, 6 Aug 2024 22:00:13 +0000 (00:00 +0200)
committerGitHub <noreply@github.com>
Tue, 6 Aug 2024 22:00:13 +0000 (00:00 +0200)
Signed-off-by: Jan N. Klug <github@klug.nrw>
bundles/org.openhab.binding.awattar/README.md
bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandler.java
bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/config/config.xml
bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.awattar/src/main/resources/OH-INF/update/update.xml [new file with mode: 0644]
bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerRefreshTest.java
bundles/org.openhab.binding.awattar/src/test/java/org/openhab/binding/awattar/internal/handler/AwattarBridgeHandlerTest.java

index e0c227f45fdd02aee30be933ab019430cb3b118b..f68bbd6c6fa36e692d17702653496b45a1dd2466 100644 (file)
@@ -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:
index 063148bec6d5d0d240467de1b32f1482694d2ff0..f33ce3a34dea45448201efc1de669c74d56fbccb 100644 (file)
@@ -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<AwattarPrice> 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<AwattarPrice, Double> valueFunction) {
+        SortedSet<AwattarPrice> 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);
+            }
         }
     }
 }
index 10445470f928541bc4c2960793b23457e9898640..5fa754579e4bfcbca9d44a4327a83ad5d18c622e 100644 (file)
                                <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>
index 837435424b7f6c5cc1c39b1387975188dfa3a31a..47fcb5a1294015a7c7a7e3342012eb228d961514 100644 (file)
@@ -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>
 
                <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>
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 (file)
index 0000000..5ab170f
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
+
+       <thing-type uid="awattar:bridge">
+               <instruction-set targetVersion="1">
+                       <add-channel id="market-net">
+                               <type>awattar:uom-price</type>
+                               <label>Net Market Price</label>
+                               <description>Price without VAT and network charge</description>
+                       </add-channel>
+                       <add-channel id="total-net">
+                               <type>awattar:uom-price</type>
+                               <label>Net Total Price</label>
+                               <description>Price with network charge but without VAT</description>
+                       </add-channel>
+               </instruction-set>
+       </thing-type>
+
+</update:update-descriptions>
index afdf8892e999a78cc0052b90c19ed6f4faf4cbec..7cb1ccc46dbbde94e935f5760ff32a41e6d6a8c3 100644 (file)
@@ -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);
 
index b4d5ace5b8004f770101f27e93c44f0740e04e0c..b90d3f1ed0f5df4f9ec0108264772607335e9e77 100644 (file)
@@ -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