Skip to content

Commit 994a6b8

Browse files
authoredJun 23, 2024
[awattar] Add tests and improve code (openhab#16871)
* [awattar] add tests Signed-off-by: Jan N. Klug <github@klug.nrw>
1 parent 93457fe commit 994a6b8

15 files changed

+787
-227
lines changed
 

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/AwattarBestPriceResult.java

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
*/
2222
@NonNullByDefault
2323
public abstract class AwattarBestPriceResult {
24-
2524
private long start;
2625
private long end;
2726

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/AwattarBestpriceConfiguration.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@
2121
*/
2222
@NonNullByDefault
2323
public class AwattarBestpriceConfiguration {
24-
25-
public int rangeStart;
26-
public int rangeDuration;
27-
public int length;
28-
public boolean consecutive;
24+
public int rangeStart = 0;
25+
public int rangeDuration = 24;
26+
public int length = 1;
27+
public boolean consecutive = true;
2928

3029
@Override
3130
public String toString() {

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/AwattarBindingConstants.java

-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
import org.eclipse.jdt.annotation.NonNullByDefault;
1616
import org.openhab.core.thing.ThingTypeUID;
17-
import org.openhab.core.thing.type.ChannelGroupTypeUID;
1817

1918
/**
2019
* The {@link AwattarBindingConstants} class defines common constants, which are
@@ -24,18 +23,12 @@
2423
*/
2524
@NonNullByDefault
2625
public class AwattarBindingConstants {
27-
2826
public static final String BINDING_ID = "awattar";
29-
public static final String API = "api";
3027

3128
// List of all Thing Type UIDs
3229
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
3330
public static final ThingTypeUID THING_TYPE_PRICE = new ThingTypeUID(BINDING_ID, "prices");
3431
public static final ThingTypeUID THING_TYPE_BESTPRICE = new ThingTypeUID(BINDING_ID, "bestprice");
35-
public static final ThingTypeUID THING_TYPE_BESTNEXT = new ThingTypeUID(BINDING_ID, "bestnext");
36-
37-
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_HOURLY_PRICES = new ChannelGroupTypeUID(BINDING_ID,
38-
"hourly-prices");
3932

4033
public static final String CHANNEL_GROUP_CURRENT = "current";
4134

@@ -51,7 +44,4 @@ public class AwattarBindingConstants {
5144
public static final String CHANNEL_COUNTDOWN = "countdown";
5245
public static final String CHANNEL_REMAINING = "remaining";
5346
public static final String CHANNEL_HOURS = "hours";
54-
public static final String CHANNEL_DURATION = "rangeDuration";
55-
public static final String CHANNEL_LOOKUP_HOURS = "lookupHours";
56-
public static final String CHANNEL_CONSECUTIVE = "consecutive";
5747
}

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/AwattarBridgeConfiguration.java

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
*/
2222
@NonNullByDefault
2323
public class AwattarBridgeConfiguration {
24-
2524
public double basePrice;
2625
public double vatPercent;
2726
public String country = "";

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/AwattarConsecutiveBestPriceResult.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,25 @@
2828
*/
2929
@NonNullByDefault
3030
public class AwattarConsecutiveBestPriceResult extends AwattarBestPriceResult {
31-
3231
private double priceSum = 0;
3332
private int length = 0;
34-
private String hours;
35-
private ZoneId zoneId;
33+
private final String hours;
34+
private final ZoneId zoneId;
3635

3736
public AwattarConsecutiveBestPriceResult(List<AwattarPrice> prices, ZoneId zoneId) {
3837
super();
3938
this.zoneId = zoneId;
4039
StringBuilder hours = new StringBuilder();
4140
boolean second = false;
4241
for (AwattarPrice price : prices) {
43-
priceSum += price.getPrice();
42+
priceSum += price.netPrice();
4443
length++;
45-
updateStart(price.getStartTimestamp());
46-
updateEnd(price.getEndTimestamp());
44+
updateStart(price.timerange().start());
45+
updateEnd(price.timerange().end());
4746
if (second) {
4847
hours.append(',');
4948
}
50-
hours.append(getHourFrom(price.getStartTimestamp(), zoneId));
49+
hours.append(getHourFrom(price.timerange().start(), zoneId));
5150
second = true;
5251
}
5352
this.hours = hours.toString();

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/AwattarNonConsecutiveBestPriceResult.java

+8-14
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,11 @@
2929
*/
3030
@NonNullByDefault
3131
public class AwattarNonConsecutiveBestPriceResult extends AwattarBestPriceResult {
32-
33-
private List<AwattarPrice> members;
34-
private ZoneId zoneId;
32+
private final List<AwattarPrice> members;
33+
private final ZoneId zoneId;
3534
private boolean sorted = true;
3635

37-
public AwattarNonConsecutiveBestPriceResult(int size, ZoneId zoneId) {
36+
public AwattarNonConsecutiveBestPriceResult(ZoneId zoneId) {
3837
super();
3938
this.zoneId = zoneId;
4039
members = new ArrayList<>();
@@ -43,13 +42,13 @@ public AwattarNonConsecutiveBestPriceResult(int size, ZoneId zoneId) {
4342
public void addMember(AwattarPrice member) {
4443
sorted = false;
4544
members.add(member);
46-
updateStart(member.getStartTimestamp());
47-
updateEnd(member.getEndTimestamp());
45+
updateStart(member.timerange().start());
46+
updateEnd(member.timerange().end());
4847
}
4948

5049
@Override
5150
public boolean isActive() {
52-
return members.stream().anyMatch(x -> x.contains(Instant.now().toEpochMilli()));
51+
return members.stream().anyMatch(x -> x.timerange().contains(Instant.now().toEpochMilli()));
5352
}
5453

5554
@Override
@@ -59,12 +58,7 @@ public String toString() {
5958

6059
private void sort() {
6160
if (!sorted) {
62-
members.sort(new Comparator<>() {
63-
@Override
64-
public int compare(AwattarPrice o1, AwattarPrice o2) {
65-
return Long.compare(o1.getStartTimestamp(), o2.getStartTimestamp());
66-
}
67-
});
61+
members.sort(Comparator.comparingLong(p -> p.timerange().start()));
6862
}
6963
}
7064

@@ -77,7 +71,7 @@ public String getHours() {
7771
if (second) {
7872
res.append(',');
7973
}
80-
res.append(getHourFrom(price.getStartTimestamp(), zoneId));
74+
res.append(getHourFrom(price.timerange().start(), zoneId));
8175
second = true;
8276
}
8377
return res.toString();

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/AwattarPrice.java

+6-43
Original file line numberDiff line numberDiff line change
@@ -12,63 +12,26 @@
1212
*/
1313
package org.openhab.binding.awattar.internal;
1414

15-
import java.time.Instant;
16-
import java.time.ZoneId;
17-
import java.time.ZonedDateTime;
18-
1915
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.openhab.binding.awattar.internal.handler.TimeRange;
2017

2118
/**
2219
* Class to store hourly price data.
2320
*
2421
* @author Wolfgang Klimt - initial contribution
22+
* @author Jan N. Klug - Refactored to record
2523
*/
2624
@NonNullByDefault
27-
public class AwattarPrice implements Comparable<AwattarPrice> {
28-
private final Double price;
29-
private final long endTimestamp;
30-
private final long startTimestamp;
31-
32-
private final int hour;
33-
34-
public AwattarPrice(double price, long startTimestamp, long endTimestamp, ZoneId zoneId) {
35-
this.price = price;
36-
this.endTimestamp = endTimestamp;
37-
this.startTimestamp = startTimestamp;
38-
this.hour = ZonedDateTime.ofInstant(Instant.ofEpochMilli(startTimestamp), zoneId).getHour();
39-
}
40-
41-
public long getStartTimestamp() {
42-
return startTimestamp;
43-
}
44-
45-
public long getEndTimestamp() {
46-
return endTimestamp;
47-
}
48-
49-
public double getPrice() {
50-
return price;
51-
}
25+
public record AwattarPrice(double netPrice, double grossPrice, double netTotal, double grossTotal,
26+
TimeRange timerange) implements Comparable<AwattarPrice> {
5227

5328
@Override
5429
public String toString() {
55-
return String.format("(%1$tF %1$tR - %2$tR: %3$.3f)", startTimestamp, endTimestamp, getPrice());
56-
}
57-
58-
public int getHour() {
59-
return hour;
30+
return String.format("(%1$tF %1$tR - %2$tR: %3$.3f)", timerange.start(), timerange.end(), netPrice);
6031
}
6132

6233
@Override
6334
public int compareTo(AwattarPrice o) {
64-
return price.compareTo(o.price);
65-
}
66-
67-
public boolean isBetween(long start, long end) {
68-
return startTimestamp >= start && endTimestamp <= end;
69-
}
70-
71-
public boolean contains(long timestamp) {
72-
return startTimestamp <= timestamp && endTimestamp > timestamp;
35+
return Double.compare(netPrice, o.netPrice);
7336
}
7437
}

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/AwattarUtil.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public static long getMillisToNextMinute(int mod, TimeZoneProvider timeZoneProvi
4444
}
4545

4646
public static ZonedDateTime getCalendarForHour(int hour, ZoneId zone) {
47-
return ZonedDateTime.now(zone).truncatedTo(ChronoUnit.DAYS).plus(hour, ChronoUnit.HOURS);
47+
return ZonedDateTime.now(zone).truncatedTo(ChronoUnit.DAYS).plusHours(hour);
4848
}
4949

5050
public static DateTimeType getDateTimeType(long time, TimeZoneProvider tz) {

‎bundles/org.openhab.binding.awattar/src/main/java/org/openhab/binding/awattar/internal/handler/AwattarBestpriceHandler.java

+20-41
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@
3030
import java.util.ArrayList;
3131
import java.util.Comparator;
3232
import java.util.List;
33-
import java.util.SortedMap;
33+
import java.util.SortedSet;
3434
import java.util.concurrent.ScheduledFuture;
3535
import java.util.concurrent.TimeUnit;
36-
import java.util.stream.Collectors;
3736

3837
import org.eclipse.jdt.annotation.NonNullByDefault;
3938
import org.eclipse.jdt.annotation.Nullable;
@@ -67,12 +66,11 @@
6766
*/
6867
@NonNullByDefault
6968
public class AwattarBestpriceHandler extends BaseThingHandler {
69+
private static final int THING_REFRESH_INTERVAL = 60;
7070

7171
private final Logger logger = LoggerFactory.getLogger(AwattarBestpriceHandler.class);
7272

73-
private final int thingRefreshInterval = 60;
74-
@Nullable
75-
private ScheduledFuture<?> thingRefresher;
73+
private @Nullable ScheduledFuture<?> thingRefresher;
7674

7775
private final TimeZoneProvider timeZoneProvider;
7876

@@ -85,14 +83,8 @@ public AwattarBestpriceHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
8583
public void initialize() {
8684
AwattarBestpriceConfiguration config = getConfigAs(AwattarBestpriceConfiguration.class);
8785

88-
boolean configValid = true;
89-
9086
if (config.length >= config.rangeDuration) {
9187
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.length.value");
92-
configValid = false;
93-
}
94-
95-
if (!configValid) {
9688
return;
9789
}
9890

@@ -104,7 +96,8 @@ public void initialize() {
10496
* here
10597
*/
10698
thingRefresher = scheduler.scheduleAtFixedRate(this::refreshChannels,
107-
getMillisToNextMinute(1, timeZoneProvider), thingRefreshInterval * 1000, TimeUnit.MILLISECONDS);
99+
getMillisToNextMinute(1, timeZoneProvider), THING_REFRESH_INTERVAL * 1000,
100+
TimeUnit.MILLISECONDS);
108101
}
109102
}
110103
updateStatus(ThingStatus.UNKNOWN);
@@ -138,24 +131,24 @@ public void refreshChannel(ChannelUID channelUID) {
138131
return;
139132
}
140133
AwattarBridgeHandler bridgeHandler = (AwattarBridgeHandler) bridge.getHandler();
141-
if (bridgeHandler == null || bridgeHandler.getPriceMap() == null) {
134+
if (bridgeHandler == null || bridgeHandler.getPrices() == null) {
142135
logger.debug("No prices available, so can't refresh channel.");
143136
// no prices available, can't continue
144137
updateState(channelUID, state);
145138
return;
146139
}
147140
AwattarBestpriceConfiguration config = getConfigAs(AwattarBestpriceConfiguration.class);
148-
Timerange timerange = getRange(config.rangeStart, config.rangeDuration, bridgeHandler.getTimeZone());
149-
if (!(bridgeHandler.containsPriceFor(timerange.start) && bridgeHandler.containsPriceFor(timerange.end))) {
141+
TimeRange timerange = getRange(config.rangeStart, config.rangeDuration, bridgeHandler.getTimeZone());
142+
if (!(bridgeHandler.containsPriceFor(timerange.start()) && bridgeHandler.containsPriceFor(timerange.end()))) {
150143
updateState(channelUID, state);
151144
return;
152145
}
153146

154147
AwattarBestPriceResult result;
148+
List<AwattarPrice> range = getPriceRange(bridgeHandler, timerange);
149+
155150
if (config.consecutive) {
156-
ArrayList<AwattarPrice> range = new ArrayList<>(config.rangeDuration);
157-
range.addAll(getPriceRange(bridgeHandler, timerange,
158-
(o1, o2) -> Long.compare(o1.getStartTimestamp(), o2.getStartTimestamp())));
151+
range.sort(Comparator.comparing(AwattarPrice::timerange));
159152
AwattarConsecutiveBestPriceResult res = new AwattarConsecutiveBestPriceResult(
160153
range.subList(0, config.length), bridgeHandler.getTimeZone());
161154

@@ -168,9 +161,8 @@ public void refreshChannel(ChannelUID channelUID) {
168161
}
169162
result = res;
170163
} else {
171-
List<AwattarPrice> range = getPriceRange(bridgeHandler, timerange,
172-
(o1, o2) -> Double.compare(o1.getPrice(), o2.getPrice()));
173-
AwattarNonConsecutiveBestPriceResult res = new AwattarNonConsecutiveBestPriceResult(config.length,
164+
range.sort(Comparator.naturalOrder());
165+
AwattarNonConsecutiveBestPriceResult res = new AwattarNonConsecutiveBestPriceResult(
174166
bridgeHandler.getTimeZone());
175167
int ct = 0;
176168
for (AwattarPrice price : range) {
@@ -223,21 +215,18 @@ public void handleCommand(ChannelUID channelUID, Command command) {
223215
}
224216
}
225217

226-
private List<AwattarPrice> getPriceRange(AwattarBridgeHandler bridgeHandler, Timerange range,
227-
Comparator<AwattarPrice> comparator) {
228-
ArrayList<AwattarPrice> result = new ArrayList<>();
229-
SortedMap<Long, AwattarPrice> priceMap = bridgeHandler.getPriceMap();
230-
if (priceMap == null) {
218+
private List<AwattarPrice> getPriceRange(AwattarBridgeHandler bridgeHandler, TimeRange range) {
219+
List<AwattarPrice> result = new ArrayList<>();
220+
SortedSet<AwattarPrice> prices = bridgeHandler.getPrices();
221+
if (prices == null) {
231222
logger.debug("No prices available, can't compute ranges");
232223
return result;
233224
}
234-
result.addAll(priceMap.values().stream().filter(x -> x.isBetween(range.start, range.end))
235-
.collect(Collectors.toSet()));
236-
result.sort(comparator);
225+
result.addAll(prices.stream().filter(x -> range.contains(x.timerange())).toList());
237226
return result;
238227
}
239228

240-
private Timerange getRange(int start, int duration, ZoneId zoneId) {
229+
protected TimeRange getRange(int start, int duration, ZoneId zoneId) {
241230
ZonedDateTime startCal = getCalendarForHour(start, zoneId);
242231
ZonedDateTime endCal = startCal.plusHours(duration);
243232
ZonedDateTime now = ZonedDateTime.now(zoneId);
@@ -251,16 +240,6 @@ private Timerange getRange(int start, int duration, ZoneId zoneId) {
251240
startCal = startCal.plusDays(1);
252241
endCal = endCal.plusDays(1);
253242
}
254-
return new Timerange(startCal.toInstant().toEpochMilli(), endCal.toInstant().toEpochMilli());
255-
}
256-
257-
private class Timerange {
258-
long start;
259-
long end;
260-
261-
Timerange(long start, long end) {
262-
this.start = start;
263-
this.end = end;
264-
}
243+
return new TimeRange(startCal.toInstant().toEpochMilli(), endCal.toInstant().toEpochMilli());
265244
}
266245
}

0 commit comments

Comments
 (0)
Please sign in to comment.