mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[aWATTar] move calculation logic into best price classes (#17729)
* [aWATTar] move calculation logic into best price classes * [aWATTar] Refactor AwattarBestPriceTest and AwattarApiTest by initializing zoneId directly and removing unnecessary setup Signed-off-by: Thomas Leber <thomas@tl-photography.at> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
680f84038d
commit
a427e2aa93
@ -17,6 +17,7 @@ import static org.openhab.binding.awattar.internal.AwattarUtil.getHourFrom;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -33,23 +34,45 @@ public class AwattarConsecutiveBestPriceResult extends AwattarBestPriceResult {
|
||||
private final String hours;
|
||||
private final ZoneId zoneId;
|
||||
|
||||
public AwattarConsecutiveBestPriceResult(List<AwattarPrice> prices, ZoneId zoneId) {
|
||||
public AwattarConsecutiveBestPriceResult(List<AwattarPrice> prices, int length, ZoneId zoneId) {
|
||||
super();
|
||||
this.zoneId = zoneId;
|
||||
StringBuilder hours = new StringBuilder();
|
||||
boolean second = false;
|
||||
for (AwattarPrice price : prices) {
|
||||
|
||||
// sort the prices by timerange
|
||||
prices.sort(Comparator.comparing(AwattarPrice::timerange));
|
||||
|
||||
// calculate the range with the lowest accumulated price of length hours from the given prices
|
||||
double minPrice = Double.MAX_VALUE;
|
||||
int minIndex = 0;
|
||||
for (int i = 0; i <= prices.size() - length; i++) {
|
||||
double sum = 0;
|
||||
for (int j = 0; j < length; j++) {
|
||||
sum += prices.get(i + j).netPrice();
|
||||
}
|
||||
if (sum < minPrice) {
|
||||
minPrice = sum;
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate the accumulated price and the range of the best price
|
||||
for (int i = 0; i < length; i++) {
|
||||
AwattarPrice price = prices.get(minIndex + i);
|
||||
priceSum += price.netPrice();
|
||||
length++;
|
||||
updateStart(price.timerange().start());
|
||||
updateEnd(price.timerange().end());
|
||||
if (second) {
|
||||
hours.append(',');
|
||||
}
|
||||
hours.append(getHourFrom(price.timerange().start(), zoneId));
|
||||
second = true;
|
||||
|
||||
// create a list of hours for the best price range
|
||||
StringBuilder locHours = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (i > 0) {
|
||||
locHours.append(",");
|
||||
}
|
||||
this.hours = hours.toString();
|
||||
locHours.append(getHourFrom(prices.get(minIndex + i).timerange().start(), zoneId));
|
||||
}
|
||||
|
||||
this.hours = locHours.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,10 +84,6 @@ public class AwattarConsecutiveBestPriceResult extends AwattarBestPriceResult {
|
||||
return timestamp >= getStart() && timestamp < getEnd();
|
||||
}
|
||||
|
||||
public double getPriceSum() {
|
||||
return priceSum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{%s, %s, %.2f}", formatDate(getStart(), zoneId), formatDate(getEnd(), zoneId),
|
||||
|
@ -17,6 +17,7 @@ import static org.openhab.binding.awattar.internal.AwattarUtil.*;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@ -33,13 +34,29 @@ public class AwattarNonConsecutiveBestPriceResult extends AwattarBestPriceResult
|
||||
private final ZoneId zoneId;
|
||||
private boolean sorted = true;
|
||||
|
||||
public AwattarNonConsecutiveBestPriceResult(ZoneId zoneId) {
|
||||
public AwattarNonConsecutiveBestPriceResult(List<AwattarPrice> prices, int length, boolean inverted,
|
||||
ZoneId zoneId) {
|
||||
super();
|
||||
this.zoneId = zoneId;
|
||||
members = new ArrayList<>();
|
||||
|
||||
prices.sort(Comparator.naturalOrder());
|
||||
|
||||
// sort in descending order when inverted
|
||||
if (inverted) {
|
||||
Collections.reverse(prices);
|
||||
}
|
||||
|
||||
public void addMember(AwattarPrice member) {
|
||||
// take up to config.length prices
|
||||
for (int i = 0; i < Math.min(length, prices.size()); i++) {
|
||||
addMember(prices.get(i));
|
||||
}
|
||||
|
||||
// sort the members
|
||||
members.sort(Comparator.comparing(AwattarPrice::timerange));
|
||||
}
|
||||
|
||||
private void addMember(AwattarPrice member) {
|
||||
sorted = false;
|
||||
members.add(member);
|
||||
updateStart(member.timerange().start());
|
||||
@ -67,6 +84,7 @@ public class AwattarNonConsecutiveBestPriceResult extends AwattarBestPriceResult
|
||||
boolean second = false;
|
||||
sort();
|
||||
StringBuilder res = new StringBuilder();
|
||||
|
||||
for (AwattarPrice price : members) {
|
||||
if (second) {
|
||||
res.append(',');
|
||||
|
@ -28,8 +28,6 @@ import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
@ -128,11 +126,13 @@ public class AwattarBestPriceHandler extends BaseThingHandler {
|
||||
public void refreshChannel(ChannelUID channelUID) {
|
||||
State state = UnDefType.UNDEF;
|
||||
Bridge bridge = getBridge();
|
||||
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.bridge.missing");
|
||||
updateState(channelUID, state);
|
||||
return;
|
||||
}
|
||||
|
||||
AwattarBridgeHandler bridgeHandler = (AwattarBridgeHandler) bridge.getHandler();
|
||||
if (bridgeHandler == null || bridgeHandler.getPrices() == null) {
|
||||
logger.debug("No prices available, so can't refresh channel.");
|
||||
@ -140,8 +140,11 @@ public class AwattarBestPriceHandler extends BaseThingHandler {
|
||||
updateState(channelUID, state);
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneId zoneId = bridgeHandler.getTimeZone();
|
||||
|
||||
AwattarBestPriceConfiguration config = getConfigAs(AwattarBestPriceConfiguration.class);
|
||||
TimeRange timerange = getRange(config.rangeStart, config.rangeDuration, bridgeHandler.getTimeZone());
|
||||
TimeRange timerange = getRange(config.rangeStart, config.rangeDuration, zoneId);
|
||||
if (!(bridgeHandler.containsPriceFor(timerange))) {
|
||||
updateState(channelUID, state);
|
||||
return;
|
||||
@ -151,36 +154,11 @@ public class AwattarBestPriceHandler extends BaseThingHandler {
|
||||
List<AwattarPrice> range = getPriceRange(bridgeHandler, timerange);
|
||||
|
||||
if (config.consecutive) {
|
||||
range.sort(Comparator.comparing(AwattarPrice::timerange));
|
||||
AwattarConsecutiveBestPriceResult res = new AwattarConsecutiveBestPriceResult(
|
||||
range.subList(0, config.length), bridgeHandler.getTimeZone());
|
||||
|
||||
for (int i = 1; i <= range.size() - config.length; i++) {
|
||||
AwattarConsecutiveBestPriceResult res2 = new AwattarConsecutiveBestPriceResult(
|
||||
range.subList(i, i + config.length), bridgeHandler.getTimeZone());
|
||||
if (res2.getPriceSum() < res.getPriceSum()) {
|
||||
res = res2;
|
||||
}
|
||||
}
|
||||
result = res;
|
||||
result = new AwattarConsecutiveBestPriceResult(range, config.length, zoneId);
|
||||
} else {
|
||||
range.sort(Comparator.naturalOrder());
|
||||
|
||||
// sort in descending order when inverted
|
||||
if (config.inverted) {
|
||||
Collections.reverse(range);
|
||||
result = new AwattarNonConsecutiveBestPriceResult(range, config.length, config.inverted, zoneId);
|
||||
}
|
||||
|
||||
AwattarNonConsecutiveBestPriceResult res = new AwattarNonConsecutiveBestPriceResult(
|
||||
bridgeHandler.getTimeZone());
|
||||
|
||||
// take up to config.length prices
|
||||
for (int i = 0; i < Math.min(config.length, range.size()); i++) {
|
||||
res.addMember(range.get(i));
|
||||
}
|
||||
|
||||
result = res;
|
||||
}
|
||||
String channelId = channelUID.getIdWithoutGroup();
|
||||
long diff;
|
||||
switch (channelId) {
|
||||
|
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.binding.awattar.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.awattar.internal.handler.TimeRange;
|
||||
|
||||
/**
|
||||
* The {@link AwattarBestPriceTest} contains tests for the
|
||||
* {@link AwattarConsecutiveBestPriceResult} and {@link AwattarNonConsecutiveBestPriceResult} logic.
|
||||
*
|
||||
* @author Thomas Leber - Initial contribution
|
||||
*/
|
||||
public class AwattarBestPriceTest {
|
||||
|
||||
private ZoneId zoneId = ZoneId.of("GMT");
|
||||
|
||||
public static ZonedDateTime getCalendarForHour(int hour, ZoneId zone) {
|
||||
return ZonedDateTime.ofInstant(Instant.ofEpochSecond(1731283200L), zone).truncatedTo(ChronoUnit.HOURS)
|
||||
.plusHours(hour);
|
||||
}
|
||||
|
||||
public synchronized SortedSet<AwattarPrice> getPrices() {
|
||||
SortedSet<AwattarPrice> prices = new TreeSet<>(Comparator.comparing(AwattarPrice::timerange));
|
||||
|
||||
prices.add(new AwattarPrice(103.87, 103.87, 103.87, 103.87, new TimeRange(1731283200000L, 1731286800000L)));
|
||||
prices.add(new AwattarPrice(100.06, 100.06, 100.06, 100.06, new TimeRange(1731286800000L, 1731290400000L)));
|
||||
prices.add(new AwattarPrice(99.06, 99.06, 99.06, 99.06, new TimeRange(1731290400000L, 1731294000000L)));
|
||||
prices.add(new AwattarPrice(99.12, 99.12, 99.12, 99.12, new TimeRange(1731294000000L, 1731297600000L)));
|
||||
prices.add(new AwattarPrice(105.16, 105.16, 105.16, 105.16, new TimeRange(1731297600000L, 1731301200000L)));
|
||||
prices.add(new AwattarPrice(124.96, 124.96, 124.96, 124.96, new TimeRange(1731301200000L, 1731304800000L)));
|
||||
prices.add(new AwattarPrice(143.91, 143.91, 143.91, 143.91, new TimeRange(1731304800000L, 1731308400000L)));
|
||||
prices.add(new AwattarPrice(141.95, 141.95, 141.95, 141.95, new TimeRange(1731308400000L, 1731312000000L)));
|
||||
prices.add(new AwattarPrice(135.95, 135.95, 135.95, 135.95, new TimeRange(1731312000000L, 1731315600000L)));
|
||||
prices.add(new AwattarPrice(130.39, 130.39, 130.39, 130.39, new TimeRange(1731315600000L, 1731319200000L)));
|
||||
prices.add(new AwattarPrice(124.5, 124.5, 124.5, 124.5, new TimeRange(1731319200000L, 1731322800000L)));
|
||||
prices.add(new AwattarPrice(119.79, 119.79, 119.79, 119.79, new TimeRange(1731322800000L, 1731326400000L)));
|
||||
prices.add(new AwattarPrice(131.13, 131.13, 131.13, 131.13, new TimeRange(1731326400000L, 1731330000000L)));
|
||||
prices.add(new AwattarPrice(133.72, 133.72, 133.72, 133.72, new TimeRange(1731330000000L, 1731333600000L)));
|
||||
prices.add(new AwattarPrice(141.58, 141.58, 141.58, 141.58, new TimeRange(1731333600000L, 1731337200000L)));
|
||||
prices.add(new AwattarPrice(146.94, 146.94, 146.94, 146.94, new TimeRange(1731337200000L, 1731340800000L)));
|
||||
prices.add(new AwattarPrice(150.08, 150.08, 150.08, 150.08, new TimeRange(1731340800000L, 1731344400000L)));
|
||||
prices.add(new AwattarPrice(146.9, 146.9, 146.9, 146.9, new TimeRange(1731344400000L, 1731348000000L)));
|
||||
prices.add(new AwattarPrice(139.87, 139.87, 139.87, 139.87, new TimeRange(1731348000000L, 1731351600000L)));
|
||||
prices.add(new AwattarPrice(123.78, 123.78, 123.78, 123.78, new TimeRange(1731351600000L, 1731355200000L)));
|
||||
prices.add(new AwattarPrice(119.02, 119.02, 119.02, 119.02, new TimeRange(1731355200000L, 1731358800000L)));
|
||||
prices.add(new AwattarPrice(116.87, 116.87, 116.87, 116.87, new TimeRange(1731358800000L, 1731362400000L)));
|
||||
prices.add(new AwattarPrice(109.72, 109.72, 109.72, 109.72, new TimeRange(1731362400000L, 1731366000000L)));
|
||||
prices.add(new AwattarPrice(107.89, 107.89, 107.89, 107.89, new TimeRange(1731366000000L, 1731369600000L)));
|
||||
|
||||
return prices;
|
||||
}
|
||||
|
||||
@Test
|
||||
void AwattarConsecutiveBestPriceResult() {
|
||||
int length = 8;
|
||||
|
||||
List<AwattarPrice> range = new ArrayList<>(getPrices());
|
||||
|
||||
range.sort(Comparator.comparing(AwattarPrice::timerange));
|
||||
AwattarConsecutiveBestPriceResult result = new AwattarConsecutiveBestPriceResult(range, length, zoneId);
|
||||
assertEquals("00,01,02,03,04,05,06,07", result.getHours());
|
||||
}
|
||||
|
||||
@Test
|
||||
void AwattarNonConsecutiveBestPriceResult_nonInverted() {
|
||||
int length = 6;
|
||||
boolean inverted = false;
|
||||
|
||||
List<AwattarPrice> range = new ArrayList<>(getPrices());
|
||||
|
||||
range.sort(Comparator.comparing(AwattarPrice::timerange));
|
||||
AwattarNonConsecutiveBestPriceResult result = new AwattarNonConsecutiveBestPriceResult(range, length, inverted,
|
||||
zoneId);
|
||||
assertEquals("00,01,02,03,04,23", result.getHours());
|
||||
}
|
||||
|
||||
@Test
|
||||
void AwattarNonConsecutiveBestPriceResult_inverted() {
|
||||
int length = 4;
|
||||
boolean inverted = true;
|
||||
|
||||
List<AwattarPrice> range = new ArrayList<>(getPrices());
|
||||
|
||||
range.sort(Comparator.comparing(AwattarPrice::timerange));
|
||||
AwattarNonConsecutiveBestPriceResult result = new AwattarNonConsecutiveBestPriceResult(range, length, inverted,
|
||||
zoneId);
|
||||
assertEquals("06,15,16,17", result.getHours());
|
||||
}
|
||||
}
|
@ -45,8 +45,6 @@ import org.mockito.quality.Strictness;
|
||||
import org.openhab.binding.awattar.internal.AwattarBridgeConfiguration;
|
||||
import org.openhab.binding.awattar.internal.AwattarPrice;
|
||||
import org.openhab.binding.awattar.internal.api.AwattarApi.AwattarApiException;
|
||||
import org.openhab.binding.awattar.internal.handler.AwattarBridgeHandler;
|
||||
import org.openhab.binding.awattar.internal.handler.AwattarBridgeHandlerTest;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.test.java.JavaTest;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user