[solarforecast] Avoid past data updates (#16996)

Signed-off-by: Bernd Weymann <bernd.weymann@gmail.com>
This commit is contained in:
Bernd Weymann 2024-08-22 22:54:30 +02:00 committed by GitHub
parent 1793afc871
commit c108049be8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 82 additions and 43 deletions

View File

@ -46,6 +46,7 @@ import org.slf4j.LoggerFactory;
* The {@link ForecastSolarObject} holds complete data for forecast
*
* @author Bernd Weymann - Initial contribution
* @author Bernd Weymann - TimeSeries delivers only future values, otherwise past values are overwritten
*/
@NonNullByDefault
public class ForecastSolarObject implements SolarForecast {
@ -157,8 +158,12 @@ public class ForecastSolarObject implements SolarForecast {
@Override
public TimeSeries getEnergyTimeSeries(QueryMode mode) {
TimeSeries ts = new TimeSeries(Policy.REPLACE);
Instant now = Instant.now(Utils.getClock());
wattHourMap.forEach((timestamp, energy) -> {
ts.add(timestamp.toInstant(), Utils.getEnergyState(energy / 1000.0));
Instant entryTimestamp = timestamp.toInstant();
if (Utils.isAfterOrEqual(entryTimestamp, now)) {
ts.add(entryTimestamp, Utils.getEnergyState(energy / 1000.0));
}
});
return ts;
}
@ -206,8 +211,12 @@ public class ForecastSolarObject implements SolarForecast {
@Override
public TimeSeries getPowerTimeSeries(QueryMode mode) {
TimeSeries ts = new TimeSeries(Policy.REPLACE);
Instant now = Instant.now(Utils.getClock());
wattMap.forEach((timestamp, power) -> {
ts.add(timestamp.toInstant(), Utils.getPowerState(power / 1000.0));
Instant entryTimestamp = timestamp.toInstant();
if (Utils.isAfterOrEqual(entryTimestamp, now)) {
ts.add(entryTimestamp, Utils.getPowerState(power / 1000.0));
}
});
return ts;
}

View File

@ -46,6 +46,7 @@ import org.slf4j.LoggerFactory;
* The {@link SolcastObject} holds complete data for forecast
*
* @author Bernd Weymann - Initial contribution
* @author Bernd Weymann - TimeSeries delivers only future values, otherwise past values are overwritten
*/
@NonNullByDefault
public class SolcastObject implements SolarForecast {
@ -214,8 +215,12 @@ public class SolcastObject implements SolarForecast {
public TimeSeries getEnergyTimeSeries(QueryMode mode) {
TreeMap<ZonedDateTime, Double> dtm = getDataMap(mode);
TimeSeries ts = new TimeSeries(Policy.REPLACE);
Instant now = Instant.now(Utils.getClock());
dtm.forEach((timestamp, energy) -> {
ts.add(timestamp.toInstant(), Utils.getEnergyState(getActualEnergyValue(timestamp, mode)));
Instant entryTimestamp = timestamp.toInstant();
if (Utils.isAfterOrEqual(entryTimestamp, now)) {
ts.add(entryTimestamp, Utils.getEnergyState(getActualEnergyValue(timestamp, mode)));
}
});
return ts;
}
@ -264,8 +269,12 @@ public class SolcastObject implements SolarForecast {
public TimeSeries getPowerTimeSeries(QueryMode mode) {
TreeMap<ZonedDateTime, Double> dtm = getDataMap(mode);
TimeSeries ts = new TimeSeries(Policy.REPLACE);
Instant now = Instant.now(Utils.getClock());
dtm.forEach((timestamp, power) -> {
ts.add(timestamp.toInstant(), Utils.getPowerState(power));
Instant entryTimestamp = timestamp.toInstant();
if (Utils.isAfterOrEqual(entryTimestamp, now)) {
ts.add(entryTimestamp, Utils.getPowerState(power));
}
});
return ts;
}

View File

@ -29,6 +29,7 @@ import javax.measure.quantity.Energy;
import javax.measure.quantity.Power;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openhab.binding.solarforecast.internal.SolarForecastBindingConstants;
import org.openhab.binding.solarforecast.internal.SolarForecastException;
@ -68,6 +69,14 @@ class ForecastSolarTest {
public static final String NO_GORECAST_INDICATOR = "No forecast data";
public static final String DAY_MISSING_INDICATOR = "not available in forecast";
@BeforeAll
static void setFixedTime() {
// Instant matching the date of test resources
String fixedInstant = "2022-07-17T15:00:00Z";
Clock fixedClock = Clock.fixed(Instant.parse(fixedInstant), TEST_ZONE);
Utils.setClock(fixedClock);
}
@Test
void testForecastObject() {
String content = FileReader.readFileInString("src/test/resources/forecastsolar/result.json");
@ -345,16 +354,19 @@ class ForecastSolarTest {
ForecastSolarObject fo = new ForecastSolarObject("fs-test", content, queryDateTime.toInstant());
TimeSeries powerSeries = fo.getPowerTimeSeries(QueryMode.Average);
assertEquals(36, powerSeries.size()); // 18 values each day for 2 days
Instant now = Instant.now(Utils.getClock());
assertEquals(24, powerSeries.size());
powerSeries.getStates().forEachOrdered(entry -> {
assertTrue(Utils.isAfterOrEqual(entry.timestamp(), now));
State s = entry.state();
assertTrue(s instanceof QuantityType<?>);
assertEquals("kW", ((QuantityType<?>) s).getUnit().toString());
});
TimeSeries energySeries = fo.getEnergyTimeSeries(QueryMode.Average);
assertEquals(36, energySeries.size());
assertEquals(24, energySeries.size());
energySeries.getStates().forEachOrdered(entry -> {
assertTrue(Utils.isAfterOrEqual(entry.timestamp(), now));
State s = entry.state();
assertTrue(s instanceof QuantityType<?>);
assertEquals("kWh", ((QuantityType<?>) s).getUnit().toString());
@ -363,10 +375,6 @@ class ForecastSolarTest {
@Test
void testPowerTimeSeries() {
// Instant matching the date of test resources
String fixedInstant = "2022-07-17T15:00:00Z";
Clock fixedClock = Clock.fixed(Instant.parse(fixedInstant), TEST_ZONE);
Utils.setClock(fixedClock);
ForecastSolarBridgeHandler fsbh = new ForecastSolarBridgeHandler(
new BridgeImpl(SolarForecastBindingConstants.FORECAST_SOLAR_SITE, "bridge"),
Optional.of(PointType.valueOf("1,2")));
@ -398,10 +406,6 @@ class ForecastSolarTest {
@Test
void testCommonForecastStartEnd() {
// Instant matching the date of test resources
String fixedInstant = "2022-07-17T15:00:00Z";
Clock fixedClock = Clock.fixed(Instant.parse(fixedInstant), TEST_ZONE);
Utils.setClock(fixedClock);
ForecastSolarBridgeHandler fsbh = new ForecastSolarBridgeHandler(
new BridgeImpl(SolarForecastBindingConstants.FORECAST_SOLAR_SITE, "bridge"),
Optional.of(PointType.valueOf("1,2")));
@ -447,10 +451,6 @@ class ForecastSolarTest {
@Test
void testActions() {
// Instant matching the date of test resources
String fixedInstant = "2022-07-17T15:00:00Z";
Clock fixedClock = Clock.fixed(Instant.parse(fixedInstant), TEST_ZONE);
Utils.setClock(fixedClock);
ForecastSolarBridgeHandler fsbh = new ForecastSolarBridgeHandler(
new BridgeImpl(SolarForecastBindingConstants.FORECAST_SOLAR_SITE, "bridge"),
Optional.of(PointType.valueOf("1,2")));
@ -486,10 +486,6 @@ class ForecastSolarTest {
@Test
void testEnergyTimeSeries() {
// Instant matching the date of test resources
String fixedInstant = "2022-07-17T15:00:00Z";
Clock fixedClock = Clock.fixed(Instant.parse(fixedInstant), TEST_ZONE);
Utils.setClock(fixedClock);
ForecastSolarBridgeHandler fsbh = new ForecastSolarBridgeHandler(
new BridgeImpl(SolarForecastBindingConstants.FORECAST_SOLAR_SITE, "bridge"),
Optional.of(PointType.valueOf("1,2")));
@ -521,10 +517,6 @@ class ForecastSolarTest {
@Test
void testCalmDown() {
// Instant matching the date of test resources
String fixedInstant = "2022-07-17T15:00:00Z";
Clock fixedClock = Clock.fixed(Instant.parse(fixedInstant), TEST_ZONE);
Utils.setClock(fixedClock);
ForecastSolarBridgeHandler fsbh = new ForecastSolarBridgeHandler(
new BridgeImpl(SolarForecastBindingConstants.FORECAST_SOLAR_SITE, "bridge"),
Optional.of(PointType.valueOf("1,2")));
@ -555,8 +547,8 @@ class ForecastSolarTest {
assertEquals(ThingStatusDetail.COMMUNICATION_ERROR, cm.getStatus().getStatusDetail(), "Offline");
// forward Clock to get ONLINE again
fixedInstant = "2022-07-17T16:15:00Z";
fixedClock = Clock.fixed(Instant.parse(fixedInstant), ZoneId.of("UTC"));
String fixedInstant = "2022-07-17T16:15:00Z";
Clock fixedClock = Clock.fixed(Instant.parse(fixedInstant), ZoneId.of("UTC"));
Utils.setClock(fixedClock);
fsbh.handleCommand(
new ChannelUID("solarforecast:fs-site:bridge:" + SolarForecastBindingConstants.CHANNEL_ENERGY_ACTUAL),

View File

@ -14,6 +14,7 @@ package org.openhab.binding.solarforecast;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
@ -28,6 +29,7 @@ import javax.measure.quantity.Energy;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openhab.binding.solarforecast.internal.SolarForecastBindingConstants;
import org.openhab.binding.solarforecast.internal.SolarForecastException;
@ -38,6 +40,7 @@ import org.openhab.binding.solarforecast.internal.solcast.SolcastObject.QueryMod
import org.openhab.binding.solarforecast.internal.solcast.handler.SolcastBridgeHandler;
import org.openhab.binding.solarforecast.internal.solcast.handler.SolcastPlaneHandler;
import org.openhab.binding.solarforecast.internal.solcast.handler.SolcastPlaneMock;
import org.openhab.binding.solarforecast.internal.utils.Utils;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.internal.BridgeImpl;
@ -59,6 +62,21 @@ class SolcastTest {
public static final String TOO_LATE_INDICATOR = "too late";
public static final String DAY_MISSING_INDICATOR = "not available in forecast";
@BeforeAll
static void setFixedTimeJul17() {
// Instant matching the date of test resources
Instant fixedInstant = Instant.parse("2022-07-17T21:00:00Z");
Clock fixedClock = Clock.fixed(fixedInstant, TEST_ZONE);
Utils.setClock(fixedClock);
}
static void setFixedTimeJul18() {
// Instant matching the date of test resources
Instant fixedInstant = Instant.parse("2022-07-18T14:23:00Z");
Clock fixedClock = Clock.fixed(fixedInstant, TEST_ZONE);
Utils.setClock(fixedClock);
}
/**
* "2022-07-18T00:00+02:00[Europe/Berlin]": 0,
* "2022-07-18T00:30+02:00[Europe/Berlin]": 0,
@ -497,16 +515,18 @@ class SolcastTest {
@Test
void testPowerTimeSeries() {
setFixedTimeJul18();
Instant now = Instant.now(Utils.getClock());
String content = FileReader.readFileInString("src/test/resources/solcast/estimated-actuals.json");
ZonedDateTime now = LocalDateTime.of(2022, 7, 18, 16, 23).atZone(TEST_ZONE);
SolcastObject sco = new SolcastObject("sc-test", content, now.toInstant(), TIMEZONEPROVIDER);
SolcastObject sco = new SolcastObject("sc-test", content, now, TIMEZONEPROVIDER);
content = FileReader.readFileInString("src/test/resources/solcast/forecasts.json");
sco.join(content);
TimeSeries powerSeries = sco.getPowerTimeSeries(QueryMode.Average);
List<QuantityType<?>> estimateL = new ArrayList<>();
assertEquals(672, powerSeries.size());
assertEquals(302, powerSeries.size());
powerSeries.getStates().forEachOrdered(entry -> {
assertTrue(entry.timestamp().isAfter(Instant.now(Utils.getClock())));
State s = entry.state();
assertTrue(s instanceof QuantityType<?>);
assertEquals("kW", ((QuantityType<?>) s).getUnit().toString());
@ -519,8 +539,9 @@ class SolcastTest {
TimeSeries powerSeries10 = sco.getPowerTimeSeries(QueryMode.Pessimistic);
List<QuantityType<?>> estimate10 = new ArrayList<>();
assertEquals(672, powerSeries10.size());
assertEquals(302, powerSeries10.size());
powerSeries10.getStates().forEachOrdered(entry -> {
assertTrue(entry.timestamp().isAfter(Instant.now(Utils.getClock())));
State s = entry.state();
assertTrue(s instanceof QuantityType<?>);
assertEquals("kW", ((QuantityType<?>) s).getUnit().toString());
@ -533,8 +554,9 @@ class SolcastTest {
TimeSeries powerSeries90 = sco.getPowerTimeSeries(QueryMode.Optimistic);
List<QuantityType<?>> estimate90 = new ArrayList<>();
assertEquals(672, powerSeries90.size());
assertEquals(302, powerSeries90.size());
powerSeries90.getStates().forEachOrdered(entry -> {
assertTrue(entry.timestamp().isAfter(Instant.now(Utils.getClock())));
State s = entry.state();
assertTrue(s instanceof QuantityType<?>);
assertEquals("kW", ((QuantityType<?>) s).getUnit().toString());
@ -555,16 +577,18 @@ class SolcastTest {
@Test
void testEnergyTimeSeries() {
setFixedTimeJul18();
Instant now = Instant.now(Utils.getClock());
String content = FileReader.readFileInString("src/test/resources/solcast/estimated-actuals.json");
ZonedDateTime now = LocalDateTime.of(2022, 7, 18, 16, 23).atZone(TEST_ZONE);
SolcastObject sco = new SolcastObject("sc-test", content, now.toInstant(), TIMEZONEPROVIDER);
SolcastObject sco = new SolcastObject("sc-test", content, now, TIMEZONEPROVIDER);
content = FileReader.readFileInString("src/test/resources/solcast/forecasts.json");
sco.join(content);
TimeSeries energySeries = sco.getEnergyTimeSeries(QueryMode.Average);
List<QuantityType<?>> estimateL = new ArrayList<>();
assertEquals(672, energySeries.size()); // 18 values each day for 2 days
assertEquals(302, energySeries.size()); // 48 values each day for next 7 days
energySeries.getStates().forEachOrdered(entry -> {
assertTrue(Utils.isAfterOrEqual(entry.timestamp(), now));
State s = entry.state();
assertTrue(s instanceof QuantityType<?>);
assertEquals("kWh", ((QuantityType<?>) s).getUnit().toString());
@ -577,8 +601,9 @@ class SolcastTest {
TimeSeries energySeries10 = sco.getEnergyTimeSeries(QueryMode.Pessimistic);
List<QuantityType<?>> estimate10 = new ArrayList<>();
assertEquals(672, energySeries10.size()); // 18 values each day for 2 days
assertEquals(302, energySeries10.size()); // 48 values each day for next 7 days
energySeries10.getStates().forEachOrdered(entry -> {
assertTrue(Utils.isAfterOrEqual(entry.timestamp(), now));
State s = entry.state();
assertTrue(s instanceof QuantityType<?>);
assertEquals("kWh", ((QuantityType<?>) s).getUnit().toString());
@ -591,8 +616,9 @@ class SolcastTest {
TimeSeries energySeries90 = sco.getEnergyTimeSeries(QueryMode.Optimistic);
List<QuantityType<?>> estimate90 = new ArrayList<>();
assertEquals(672, energySeries90.size()); // 18 values each day for 2 days
assertEquals(302, energySeries90.size()); // 48 values each day for next 7 days
energySeries90.getStates().forEachOrdered(entry -> {
assertTrue(Utils.isAfterOrEqual(entry.timestamp(), now));
State s = entry.state();
assertTrue(s instanceof QuantityType<?>);
assertEquals("kWh", ((QuantityType<?>) s).getUnit().toString());
@ -613,6 +639,7 @@ class SolcastTest {
@Test
void testCombinedPowerTimeSeries() {
setFixedTimeJul18();
BridgeImpl bi = new BridgeImpl(SolarForecastBindingConstants.SOLCAST_SITE, "bridge");
SolcastBridgeHandler scbh = new SolcastBridgeHandler(bi, new TimeZP());
bi.setHandler(scbh);
@ -632,8 +659,8 @@ class SolcastTest {
TimeSeries ts1 = cm.getTimeSeries("solarforecast:sc-site:bridge:average#power-estimate");
TimeSeries ts2 = cm2.getTimeSeries("solarforecast:sc-plane:thing:average#power-estimate");
assertEquals(336, ts1.size(), "TimeSeries size");
assertEquals(336, ts2.size(), "TimeSeries size");
assertEquals(302, ts1.size(), "TimeSeries size");
assertEquals(302, ts2.size(), "TimeSeries size");
Iterator<TimeSeries.Entry> iter1 = ts1.getStates().iterator();
Iterator<TimeSeries.Entry> iter2 = ts2.getStates().iterator();
while (iter1.hasNext()) {
@ -651,6 +678,7 @@ class SolcastTest {
@Test
void testCombinedEnergyTimeSeries() {
setFixedTimeJul18();
BridgeImpl bi = new BridgeImpl(SolarForecastBindingConstants.SOLCAST_SITE, "bridge");
SolcastBridgeHandler scbh = new SolcastBridgeHandler(bi, new TimeZP());
bi.setHandler(scbh);
@ -672,8 +700,8 @@ class SolcastTest {
TimeSeries ts1 = cm.getTimeSeries("solarforecast:sc-site:bridge:average#energy-estimate");
TimeSeries ts2 = cm2.getTimeSeries("solarforecast:sc-plane:thing:average#energy-estimate");
assertEquals(336, ts1.size(), "TimeSeries size");
assertEquals(336, ts2.size(), "TimeSeries size");
assertEquals(302, ts1.size(), "TimeSeries size");
assertEquals(302, ts2.size(), "TimeSeries size");
Iterator<TimeSeries.Entry> iter1 = ts1.getStates().iterator();
Iterator<TimeSeries.Entry> iter2 = ts2.getStates().iterator();
@ -692,6 +720,7 @@ class SolcastTest {
@Test
void testSingleEnergyTimeSeries() {
setFixedTimeJul18();
BridgeImpl bi = new BridgeImpl(SolarForecastBindingConstants.SOLCAST_SITE, "bridge");
SolcastBridgeHandler scbh = new SolcastBridgeHandler(bi, new TimeZP());
bi.setHandler(scbh);
@ -707,7 +736,7 @@ class SolcastTest {
scbh.getData();
TimeSeries ts1 = cm.getTimeSeries("solarforecast:sc-site:bridge:average#energy-estimate");
assertEquals(336, ts1.size(), "TimeSeries size");
assertEquals(302, ts1.size(), "TimeSeries size");
Iterator<TimeSeries.Entry> iter1 = ts1.getStates().iterator();
while (iter1.hasNext()) {
TimeSeries.Entry e1 = iter1.next();