Return units in persistence extension commands and support future persisted states (#3736)

* persistence extensions future and QuantityTypes

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
This commit is contained in:
Mark Herwege 2024-05-02 11:47:12 +02:00 committed by GitHub
parent eb2339907f
commit 1b503afdbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 3757 additions and 1268 deletions

View File

@ -18,6 +18,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -25,8 +26,8 @@ import org.openhab.core.items.Item;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.FilterCriteria.Ordering;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.ModifiablePersistenceService;
import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.persistence.QueryablePersistenceService;
import org.openhab.core.persistence.strategy.PersistenceStrategy;
import org.openhab.core.types.State;
@ -36,7 +37,7 @@ import org.openhab.core.types.State;
* @author Florian Binder - Initial contribution
*/
@NonNullByDefault
public class TestCachedValuesPersistenceService implements QueryablePersistenceService {
public class TestCachedValuesPersistenceService implements ModifiablePersistenceService {
public static final String ID = "testCachedHistoricItems";
@ -45,10 +46,6 @@ public class TestCachedValuesPersistenceService implements QueryablePersistenceS
public TestCachedValuesPersistenceService() {
}
public void addHistoricItem(ZonedDateTime timestamp, State state, String itemName) {
historicItems.add(new CachedHistoricItem(timestamp, state, itemName));
}
@Override
public String getId() {
return ID;
@ -62,6 +59,20 @@ public class TestCachedValuesPersistenceService implements QueryablePersistenceS
public void store(Item item, @Nullable String alias) {
}
@Override
public void store(Item item, ZonedDateTime date, State state) {
historicItems.add(new CachedHistoricItem(date, state, item.getName()));
}
@Override
public void store(Item item, ZonedDateTime date, State state, @Nullable String alias) {
}
@Override
public boolean remove(FilterCriteria filter) throws IllegalArgumentException {
return historicItems.removeAll(StreamSupport.stream(query(filter).spliterator(), false).toList());
}
@Override
public Iterable<HistoricItem> query(FilterCriteria filter) {
Stream<HistoricItem> stream = historicItems.stream();
@ -70,16 +81,19 @@ public class TestCachedValuesPersistenceService implements QueryablePersistenceS
throw new UnsupportedOperationException("state filtering is not supported yet");
}
if (filter.getItemName() != null) {
stream = stream.filter(hi -> filter.getItemName().equals(hi.getName()));
String itemName = filter.getItemName();
if (itemName != null) {
stream = stream.filter(hi -> itemName.equals(hi.getName()));
}
if (filter.getBeginDate() != null) {
stream = stream.filter(hi -> !filter.getBeginDate().isAfter(hi.getTimestamp()));
ZonedDateTime beginDate = filter.getBeginDate();
if (beginDate != null) {
stream = stream.filter(hi -> !beginDate.isAfter(hi.getTimestamp()));
}
if (filter.getEndDate() != null) {
stream = stream.filter(hi -> !filter.getEndDate().isBefore(hi.getTimestamp()));
ZonedDateTime endDate = filter.getEndDate();
if (endDate != null) {
stream = stream.filter(hi -> !endDate.isBefore(hi.getTimestamp()));
}
if (filter.getOrdering() == Ordering.ASCENDING) {

View File

@ -12,6 +12,7 @@
*/
package org.openhab.core.persistence.extensions;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
@ -21,6 +22,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import javax.measure.Unit;
@ -45,11 +47,44 @@ import org.openhab.core.types.State;
* A simple persistence service used for unit tests
*
* @author Kai Kreuzer - Initial contribution
* @author Mark Herwege - Allow future values
*/
@NonNullByDefault
public class TestPersistenceService implements QueryablePersistenceService {
public static final String ID = "test";
public static final String SERVICE_ID = "test";
static final int SWITCH_START = -15;
static final int SWITCH_ON_1 = -15;
static final int SWITCH_ON_INTERMEDIATE_1 = -12;
static final int SWITCH_OFF_1 = -10;
static final int SWITCH_OFF_INTERMEDIATE_1 = -6;
static final int SWITCH_ON_2 = -5;
static final int SWITCH_ON_INTERMEDIATE_21 = -1;
static final int SWITCH_ON_INTERMEDIATE_22 = +1;
static final int SWITCH_OFF_2 = +5;
static final int SWITCH_OFF_INTERMEDIATE_2 = +7;
static final int SWITCH_ON_3 = +10;
static final int SWITCH_ON_INTERMEDIATE_3 = +12;
static final int SWITCH_OFF_3 = +15;
static final int SWITCH_END = +15;
static final OnOffType SWITCH_STATE = OnOffType.ON;
static final int BEFORE_START = 1940;
static final int HISTORIC_START = 1950;
static final int HISTORIC_INTERMEDIATE_VALUE_1 = 2005;
static final int HISTORIC_INTERMEDIATE_VALUE_2 = 2011;
static final int HISTORIC_END = 2012;
static final int HISTORIC_INTERMEDIATE_NOVALUE_3 = 2019;
static final int HISTORIC_INTERMEDIATE_NOVALUE_4 = 2021;
static final int FUTURE_INTERMEDIATE_NOVALUE_1 = 2051;
static final int FUTURE_INTERMEDIATE_NOVALUE_2 = 2056;
static final int FUTURE_START = 2060;
static final int FUTURE_INTERMEDIATE_VALUE_3 = 2070;
static final int FUTURE_INTERMEDIATE_VALUE_4 = 2077;
static final int FUTURE_END = 2100;
static final int AFTER_END = 2110;
static final DecimalType STATE = new DecimalType(HISTORIC_END);
private final ItemRegistry itemRegistry;
@ -59,7 +94,7 @@ public class TestPersistenceService implements QueryablePersistenceService {
@Override
public String getId() {
return ID;
return SERVICE_ID;
}
@Override
@ -73,18 +108,17 @@ public class TestPersistenceService implements QueryablePersistenceService {
@Override
public Iterable<HistoricItem> query(FilterCriteria filter) {
if (PersistenceExtensionsTest.TEST_SWITCH.equals(filter.getItemName())) {
ZonedDateTime now = ZonedDateTime.now().truncatedTo(ChronoUnit.MINUTES),
nowMinusFifteenHours = now.minusHours(15),
beginDate = filter.getBeginDate() != null ? filter.getBeginDate() : nowMinusFifteenHours,
endDate = filter.getEndDate() != null ? filter.getEndDate() : now;
if (endDate.isBefore(beginDate)) {
return List.of();
}
ZonedDateTime now = ZonedDateTime.now().truncatedTo(ChronoUnit.MINUTES);
ZonedDateTime nowMinusHours = now.plusHours(SWITCH_START);
ZonedDateTime endDate = filter.getEndDate();
endDate = endDate != null ? endDate : now;
ZonedDateTime beginDate = filter.getBeginDate();
beginDate = beginDate != null ? beginDate : endDate.isAfter(now) ? now : endDate.minusHours(1);
List<HistoricItem> results = new ArrayList<>(16);
for (int i = 0; i <= 15; i++) {
final int hours = i;
final ZonedDateTime theDate = nowMinusFifteenHours.plusHours(hours);
List<HistoricItem> results = new ArrayList<>(31);
for (int i = SWITCH_START; i <= SWITCH_END; i++) {
final int hour = i;
final ZonedDateTime theDate = nowMinusHours.plusHours(i - SWITCH_START);
if (!theDate.isBefore(beginDate) && !theDate.isAfter(endDate)) {
results.add(new HistoricItem() {
@Override
@ -94,7 +128,8 @@ public class TestPersistenceService implements QueryablePersistenceService {
@Override
public State getState() {
return OnOffType.from(hours < 5 || hours > 10);
return OnOffType.from(hour < SWITCH_OFF_1 || (hour >= SWITCH_ON_2 && hour < SWITCH_OFF_2)
|| hour >= SWITCH_ON_3);
}
@Override
@ -117,22 +152,27 @@ public class TestPersistenceService implements QueryablePersistenceService {
}
return stream.toList();
} else {
int startValue = 1950;
int endValue = 2012;
int startValue = HISTORIC_START;
int endValue = FUTURE_END;
if (filter.getBeginDate() != null) {
startValue = filter.getBeginDate().getYear();
ZonedDateTime beginDate = filter.getBeginDate();
if (beginDate != null && beginDate.getYear() >= startValue) {
startValue = beginDate.getYear();
}
if (filter.getEndDate() != null) {
endValue = filter.getEndDate().getYear();
ZonedDateTime endDate = filter.getEndDate();
if (endDate != null && endDate.getYear() <= endValue) {
endValue = endDate.getYear();
}
if (endValue <= startValue || startValue < 1950) {
if (endValue <= startValue) {
return List.of();
}
List<HistoricItem> results = new ArrayList<>(endValue - startValue);
for (int i = startValue; i <= endValue; i++) {
if (i > HISTORIC_END && i < FUTURE_START) {
continue;
}
final int year = i;
results.add(new HistoricItem() {
@Override
@ -182,4 +222,46 @@ public class TestPersistenceService implements QueryablePersistenceService {
public List<PersistenceStrategy> getDefaultStrategies() {
return List.of();
}
static OnOffType switchValue(int hour) {
return (hour >= SWITCH_ON_1 && hour < SWITCH_OFF_1) || (hour >= SWITCH_ON_2 && hour < SWITCH_OFF_2)
|| (hour >= SWITCH_ON_3 && hour < SWITCH_OFF_3) ? OnOffType.ON : OnOffType.OFF;
}
static DecimalType value(long year) {
if (year < HISTORIC_START) {
return DecimalType.ZERO;
} else if (year <= HISTORIC_END) {
return new DecimalType(year);
} else if (year < FUTURE_START) {
return new DecimalType(HISTORIC_END);
} else if (year <= FUTURE_END) {
return new DecimalType(year);
} else {
return new DecimalType(FUTURE_END);
}
}
static double average(@Nullable Integer beginYear, @Nullable Integer endYear) {
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime beginDate = beginYear != null
? ZonedDateTime.of(beginYear >= HISTORIC_START ? beginYear : HISTORIC_START, 1, 1, 0, 0, 0, 0,
ZoneId.systemDefault())
: now;
ZonedDateTime endDate = endYear != null ? ZonedDateTime.of(endYear, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault())
: now;
int begin = beginYear != null ? beginYear : now.getYear() + 1;
int end = endYear != null ? endYear : now.getYear();
long sum = LongStream.range(begin, end).map(y -> value(y).longValue() * Duration
.between(ZonedDateTime.of(Long.valueOf(y).intValue(), 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()),
ZonedDateTime.of(Long.valueOf(y + 1).intValue(), 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()))
.toMillis()).sum();
sum += beginYear == null ? value(now.getYear()).longValue() * Duration
.between(now, ZonedDateTime.of(now.getYear() + 1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault())).toMillis()
: 0;
sum += endYear == null ? value(now.getYear()).longValue() * Duration
.between(ZonedDateTime.of(now.getYear(), 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), now).toMillis() : 0;
long duration = Duration.between(beginDate, endDate).toMillis();
return 1.0 * sum / duration;
}
}