Improve existing and add new persistence filters (#3642)
* Improve existing and add new persistence filters * include filter Signed-off-by: Jan N. Klug <github@klug.nrw>pull/3675/head
parent
c2e81a13fd
commit
d64bd3b90b
|
@ -27,17 +27,34 @@ Filter:
|
|||
;
|
||||
|
||||
FilterDetails:
|
||||
ThresholdFilter | TimeFilter
|
||||
ThresholdFilter | TimeFilter | EqualsFilter | NotEqualsFilter | IncludeFilter | NotIncludeFilter
|
||||
;
|
||||
|
||||
ThresholdFilter:
|
||||
'>' value=DECIMAL unit=STRING
|
||||
'>' (relative?='%')? value=DECIMAL unit=STRING?
|
||||
;
|
||||
|
||||
TimeFilter:
|
||||
value=INT unit=('s' | 'm' | 'h' | 'd')
|
||||
'T' value=INT unit=('s' | 'm' | 'h' | 'd')
|
||||
;
|
||||
|
||||
EqualsFilter:
|
||||
'=' values+=STRING (',' values+=STRING)*
|
||||
;
|
||||
|
||||
NotEqualsFilter:
|
||||
'!' values+=STRING (',' values+=STRING)*
|
||||
;
|
||||
|
||||
IncludeFilter:
|
||||
'[]' lower=DECIMAL upper=DECIMAL unit=STRING?
|
||||
;
|
||||
|
||||
NotIncludeFilter:
|
||||
'][' lower=DECIMAL upper=DECIMAL unit=STRING?
|
||||
;
|
||||
|
||||
|
||||
PersistenceConfiguration:
|
||||
items+=(AllConfig | ItemConfig | GroupConfig) (',' items+=(AllConfig | ItemConfig | GroupConfig))* ('->' alias=STRING)?
|
||||
((':' ('strategy' '=' strategies+=[Strategy|ID] (',' strategies+=[Strategy|ID])*)?
|
||||
|
|
|
@ -26,9 +26,13 @@ import org.openhab.core.model.core.ModelRepository;
|
|||
import org.openhab.core.model.core.ModelRepositoryChangeListener;
|
||||
import org.openhab.core.model.persistence.persistence.AllConfig;
|
||||
import org.openhab.core.model.persistence.persistence.CronStrategy;
|
||||
import org.openhab.core.model.persistence.persistence.EqualsFilter;
|
||||
import org.openhab.core.model.persistence.persistence.Filter;
|
||||
import org.openhab.core.model.persistence.persistence.GroupConfig;
|
||||
import org.openhab.core.model.persistence.persistence.IncludeFilter;
|
||||
import org.openhab.core.model.persistence.persistence.ItemConfig;
|
||||
import org.openhab.core.model.persistence.persistence.NotEqualsFilter;
|
||||
import org.openhab.core.model.persistence.persistence.NotIncludeFilter;
|
||||
import org.openhab.core.model.persistence.persistence.PersistenceConfiguration;
|
||||
import org.openhab.core.model.persistence.persistence.PersistenceModel;
|
||||
import org.openhab.core.model.persistence.persistence.Strategy;
|
||||
|
@ -40,7 +44,9 @@ import org.openhab.core.persistence.config.PersistenceAllConfig;
|
|||
import org.openhab.core.persistence.config.PersistenceConfig;
|
||||
import org.openhab.core.persistence.config.PersistenceGroupConfig;
|
||||
import org.openhab.core.persistence.config.PersistenceItemConfig;
|
||||
import org.openhab.core.persistence.filter.PersistenceEqualsFilter;
|
||||
import org.openhab.core.persistence.filter.PersistenceFilter;
|
||||
import org.openhab.core.persistence.filter.PersistenceIncludeFilter;
|
||||
import org.openhab.core.persistence.filter.PersistenceThresholdFilter;
|
||||
import org.openhab.core.persistence.filter.PersistenceTimeFilter;
|
||||
import org.openhab.core.persistence.registry.PersistenceServiceConfiguration;
|
||||
|
@ -175,13 +181,21 @@ public class PersistenceModelManager extends AbstractProvider<PersistenceService
|
|||
}
|
||||
|
||||
private PersistenceFilter mapFilter(Filter filter) {
|
||||
if (filter.getDefinition() instanceof TimeFilter) {
|
||||
TimeFilter timeFilter = (TimeFilter) filter.getDefinition();
|
||||
if (filter.getDefinition() instanceof TimeFilter timeFilter) {
|
||||
return new PersistenceTimeFilter(filter.getName(), timeFilter.getValue(), timeFilter.getUnit());
|
||||
} else if (filter.getDefinition() instanceof ThresholdFilter) {
|
||||
ThresholdFilter thresholdFilter = (ThresholdFilter) filter.getDefinition();
|
||||
} else if (filter.getDefinition() instanceof ThresholdFilter thresholdFilter) {
|
||||
return new PersistenceThresholdFilter(filter.getName(), thresholdFilter.getValue(),
|
||||
thresholdFilter.getUnit());
|
||||
thresholdFilter.getUnit(), thresholdFilter.isRelative());
|
||||
} else if (filter.getDefinition() instanceof EqualsFilter equalsFilter) {
|
||||
return new PersistenceEqualsFilter(filter.getName(), equalsFilter.getValues(), false);
|
||||
} else if (filter.getDefinition() instanceof NotEqualsFilter notEqualsFilter) {
|
||||
return new PersistenceEqualsFilter(filter.getName(), notEqualsFilter.getValues(), true);
|
||||
} else if (filter.getDefinition() instanceof IncludeFilter includeFilter) {
|
||||
return new PersistenceIncludeFilter(filter.getName(), includeFilter.getLower(), includeFilter.getUpper(),
|
||||
includeFilter.getUnit(), false);
|
||||
} else if (filter.getDefinition() instanceof NotIncludeFilter notIncludeFilter) {
|
||||
return new PersistenceIncludeFilter(filter.getName(), notIncludeFilter.getLower(),
|
||||
notIncludeFilter.getUpper(), notIncludeFilter.getUnit(), true);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown filter type " + filter.getClass());
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
package org.openhab.core.persistence.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@link org.openhab.core.persistence.dto.PersistenceFilterDTO} is used for transferring persistence filter
|
||||
|
@ -21,7 +22,24 @@ import java.math.BigDecimal;
|
|||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
public class PersistenceFilterDTO {
|
||||
public String name = "";
|
||||
public BigDecimal value = BigDecimal.ZERO;
|
||||
public String unit = "";
|
||||
public String name;
|
||||
|
||||
// threshold and time
|
||||
public BigDecimal value;
|
||||
|
||||
// threshold
|
||||
public Boolean relative;
|
||||
|
||||
// threshold, include/exclude
|
||||
public String unit;
|
||||
|
||||
// include/exclude
|
||||
public BigDecimal lower;
|
||||
public BigDecimal upper;
|
||||
|
||||
// equals/not equals
|
||||
public List<String> values;
|
||||
|
||||
// equals/not equals, include/exclude
|
||||
public Boolean inverted;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ public class PersistenceServiceConfigurationDTO {
|
|||
public Collection<PersistenceCronStrategyDTO> cronStrategies = List.of();
|
||||
public Collection<PersistenceFilterDTO> thresholdFilters = List.of();
|
||||
public Collection<PersistenceFilterDTO> timeFilters = List.of();
|
||||
public Collection<PersistenceFilterDTO> equalsFilters = List.of();
|
||||
public Collection<PersistenceFilterDTO> includeFilters = List.of();
|
||||
|
||||
public boolean editable = false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2023 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.core.persistence.filter;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.items.Item;
|
||||
|
||||
/**
|
||||
* The {@link PersistenceEqualsFilter} is a filter that allows only specific values to pass
|
||||
* <p />
|
||||
* The filter returns {@code false} if the string representation of the item's state is not in the given list
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PersistenceEqualsFilter extends PersistenceFilter {
|
||||
private final Collection<String> values;
|
||||
private final boolean inverted;
|
||||
|
||||
public PersistenceEqualsFilter(String name, Collection<String> values, boolean inverted) {
|
||||
super(name);
|
||||
this.values = values;
|
||||
this.inverted = inverted;
|
||||
}
|
||||
|
||||
public Collection<String> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public boolean getInverted() {
|
||||
return inverted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Item item) {
|
||||
return values.contains(item.getState().toFullString()) != inverted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persisted(Item item) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s [name=%s, value=%s, inverted=]", getClass().getSimpleName(), getName(), values);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2023 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.core.persistence.filter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PersistenceIncludeFilter} is a filter that allows only specific values to pass
|
||||
* <p />
|
||||
* The filter returns {@code false} if the string representation of the item's state is not in the given list
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PersistenceIncludeFilter extends PersistenceFilter {
|
||||
private final Logger logger = LoggerFactory.getLogger(PersistenceIncludeFilter.class);
|
||||
|
||||
private final BigDecimal lower;
|
||||
private final BigDecimal upper;
|
||||
private final String unit;
|
||||
private final boolean inverted;
|
||||
|
||||
public PersistenceIncludeFilter(String name, BigDecimal lower, BigDecimal upper, String unit, boolean inverted) {
|
||||
super(name);
|
||||
this.lower = lower;
|
||||
this.upper = upper;
|
||||
this.unit = unit;
|
||||
this.inverted = inverted;
|
||||
}
|
||||
|
||||
public BigDecimal getLower() {
|
||||
return lower;
|
||||
}
|
||||
|
||||
public BigDecimal getUpper() {
|
||||
return upper;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public boolean getInverted() {
|
||||
return inverted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Item item) {
|
||||
State state = item.getState();
|
||||
BigDecimal compareValue = null;
|
||||
if (state instanceof DecimalType decimalType) {
|
||||
compareValue = decimalType.toBigDecimal();
|
||||
} else if (state instanceof QuantityType<?> quantityType) {
|
||||
if (!unit.isBlank()) {
|
||||
QuantityType<?> convertedQuantity = quantityType.toUnit(unit);
|
||||
if (convertedQuantity != null) {
|
||||
compareValue = convertedQuantity.toBigDecimal();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compareValue == null) {
|
||||
logger.warn("Cannot compare {} to range {}{} - {}{} ", state, lower, unit, upper, unit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (inverted) {
|
||||
logger.error("Compare {} {} to {} -> {}, {} -> {}", inverted, compareValue, lower,
|
||||
compareValue.compareTo(lower) <= 0, upper, compareValue.compareTo(upper) >= 0);
|
||||
|
||||
return compareValue.compareTo(lower) <= 0 || compareValue.compareTo(upper) >= 0;
|
||||
} else {
|
||||
|
||||
logger.error("Compare {} {} to {} -> {}, {} -> {}", inverted, compareValue, lower,
|
||||
compareValue.compareTo(lower) >= 0, upper, compareValue.compareTo(upper) <= 0);
|
||||
return compareValue.compareTo(lower) >= 0 && compareValue.compareTo(upper) <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persisted(Item item) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s [name=%s, lower=%s, upper=%s, unit=%s, inverted=%b]", getClass().getSimpleName(),
|
||||
getName(), lower, upper, unit, inverted);
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
/**
|
||||
* The {@link PersistenceThresholdFilter} is a filter to prevent persistence based on a threshold.
|
||||
*
|
||||
* <p />
|
||||
* The filter returns {@code false} if the new value deviates by less than {@link #value}. If unit is "%" is
|
||||
* {@code true}, the filter returns {@code false} if the relative deviation is less than {@link #value}.
|
||||
*
|
||||
|
@ -43,13 +43,15 @@ public class PersistenceThresholdFilter extends PersistenceFilter {
|
|||
|
||||
private final BigDecimal value;
|
||||
private final String unit;
|
||||
private final boolean relative;
|
||||
|
||||
private final transient Map<String, State> valueCache = new HashMap<>();
|
||||
|
||||
public PersistenceThresholdFilter(String name, BigDecimal value, String unit) {
|
||||
public PersistenceThresholdFilter(String name, BigDecimal value, String unit, boolean relative) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
this.unit = unit;
|
||||
this.relative = relative;
|
||||
}
|
||||
|
||||
public BigDecimal getValue() {
|
||||
|
@ -60,6 +62,10 @@ public class PersistenceThresholdFilter extends PersistenceFilter {
|
|||
return unit;
|
||||
}
|
||||
|
||||
public boolean isRelative() {
|
||||
return relative;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public boolean apply(Item item) {
|
||||
|
@ -78,7 +84,7 @@ public class PersistenceThresholdFilter extends PersistenceFilter {
|
|||
if (state instanceof DecimalType) {
|
||||
BigDecimal oldState = ((DecimalType) cachedState).toBigDecimal();
|
||||
BigDecimal delta = oldState.subtract(((DecimalType) state).toBigDecimal());
|
||||
if ("%".equals(unit) && !BigDecimal.ZERO.equals(oldState)) {
|
||||
if (relative && !BigDecimal.ZERO.equals(oldState)) {
|
||||
delta = delta.multiply(HUNDRED).divide(oldState, 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
return delta.abs().compareTo(value) > 0;
|
||||
|
@ -86,7 +92,7 @@ public class PersistenceThresholdFilter extends PersistenceFilter {
|
|||
try {
|
||||
QuantityType oldState = (QuantityType) cachedState;
|
||||
QuantityType delta = oldState.subtract((QuantityType) state);
|
||||
if ("%".equals(unit)) {
|
||||
if (relative) {
|
||||
if (BigDecimal.ZERO.equals(oldState.toBigDecimal())) {
|
||||
// value is different and old value is 0 -> always above relative threshold
|
||||
return true;
|
||||
|
@ -117,6 +123,7 @@ public class PersistenceThresholdFilter extends PersistenceFilter {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s [name=%s, value=%s, unit=%s]", getClass().getSimpleName(), getName(), value, unit);
|
||||
return String.format("%s [name=%s, value=%s, unit=%s, relative=%b]", getClass().getSimpleName(), getName(),
|
||||
value, unit, relative);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ import org.openhab.core.items.Item;
|
|||
|
||||
/**
|
||||
* The {@link PersistenceTimeFilter} is a filter to prevent persistence base on intervals.
|
||||
*
|
||||
* The filter returns {@link false} if the time between now and the time of the last persisted value is less than
|
||||
* <p />
|
||||
* The filter returns {@code false} if the time between now and the time of the last persisted value is less than
|
||||
* {@link #duration} {@link #unit}
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
|
|
|
@ -32,7 +32,9 @@ import org.openhab.core.persistence.dto.PersistenceCronStrategyDTO;
|
|||
import org.openhab.core.persistence.dto.PersistenceFilterDTO;
|
||||
import org.openhab.core.persistence.dto.PersistenceItemConfigurationDTO;
|
||||
import org.openhab.core.persistence.dto.PersistenceServiceConfigurationDTO;
|
||||
import org.openhab.core.persistence.filter.PersistenceEqualsFilter;
|
||||
import org.openhab.core.persistence.filter.PersistenceFilter;
|
||||
import org.openhab.core.persistence.filter.PersistenceIncludeFilter;
|
||||
import org.openhab.core.persistence.filter.PersistenceThresholdFilter;
|
||||
import org.openhab.core.persistence.filter.PersistenceTimeFilter;
|
||||
import org.openhab.core.persistence.strategy.PersistenceCronStrategy;
|
||||
|
@ -65,6 +67,10 @@ public class PersistenceServiceConfigurationDTOMapper {
|
|||
PersistenceServiceConfigurationDTOMapper::mapPersistenceThresholdFilter);
|
||||
dto.timeFilters = filterList(persistenceServiceConfiguration.getFilters(), PersistenceTimeFilter.class,
|
||||
PersistenceServiceConfigurationDTOMapper::mapPersistenceTimeFilter);
|
||||
dto.equalsFilters = filterList(persistenceServiceConfiguration.getFilters(), PersistenceEqualsFilter.class,
|
||||
PersistenceServiceConfigurationDTOMapper::mapPersistenceEqualsFilter);
|
||||
dto.includeFilters = filterList(persistenceServiceConfiguration.getFilters(), PersistenceIncludeFilter.class,
|
||||
PersistenceServiceConfigurationDTOMapper::mapPersistenceIncludeFilter);
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
@ -73,11 +79,14 @@ public class PersistenceServiceConfigurationDTOMapper {
|
|||
Map<String, PersistenceStrategy> strategyMap = dto.cronStrategies.stream()
|
||||
.collect(Collectors.toMap(e -> e.name, e -> new PersistenceCronStrategy(e.name, e.cronExpression)));
|
||||
|
||||
Map<String, PersistenceFilter> filterMap = Stream
|
||||
.concat(dto.thresholdFilters.stream().map(f -> new PersistenceThresholdFilter(f.name, f.value, f.unit)),
|
||||
dto.timeFilters.stream()
|
||||
.map(f -> new PersistenceTimeFilter(f.name, f.value.intValue(), f.unit)))
|
||||
.collect(Collectors.toMap(PersistenceFilter::getName, e -> e));
|
||||
Map<String, PersistenceFilter> filterMap = Stream.of(
|
||||
dto.thresholdFilters.stream()
|
||||
.map(f -> new PersistenceThresholdFilter(f.name, f.value, f.unit, f.relative)),
|
||||
dto.timeFilters.stream().map(f -> new PersistenceTimeFilter(f.name, f.value.intValue(), f.unit)),
|
||||
dto.equalsFilters.stream().map(f -> new PersistenceEqualsFilter(f.name, f.values, f.inverted)),
|
||||
dto.includeFilters.stream()
|
||||
.map(f -> new PersistenceIncludeFilter(f.name, f.lower, f.upper, f.unit, f.inverted)))
|
||||
.flatMap(Function.identity()).collect(Collectors.toMap(PersistenceFilter::getName, e -> e));
|
||||
|
||||
List<PersistenceStrategy> defaults = dto.defaults.stream()
|
||||
.map(str -> stringToPersistenceStrategy(str, strategyMap, dto.serviceId)).toList();
|
||||
|
@ -87,7 +96,9 @@ public class PersistenceServiceConfigurationDTOMapper {
|
|||
.map(PersistenceServiceConfigurationDTOMapper::stringToPersistenceConfig).toList();
|
||||
List<PersistenceStrategy> strategies = config.strategies.stream()
|
||||
.map(str -> stringToPersistenceStrategy(str, strategyMap, dto.serviceId)).toList();
|
||||
return new PersistenceItemConfiguration(items, config.alias, strategies, List.of());
|
||||
List<PersistenceFilter> filters = config.filters.stream()
|
||||
.map(str -> stringToPersistenceFilter(str, filterMap, dto.serviceId)).toList();
|
||||
return new PersistenceItemConfiguration(items, config.alias, strategies, filters);
|
||||
}).toList();
|
||||
|
||||
return new PersistenceServiceConfiguration(dto.serviceId, configs, defaults, strategyMap.values(),
|
||||
|
@ -121,6 +132,16 @@ public class PersistenceServiceConfigurationDTOMapper {
|
|||
throw new IllegalArgumentException("Strategy '" + string + "' unknown for service '" + serviceId + "'");
|
||||
}
|
||||
|
||||
private static PersistenceFilter stringToPersistenceFilter(String string, Map<String, PersistenceFilter> filterMap,
|
||||
String serviceId) {
|
||||
PersistenceFilter filter = filterMap.get(string);
|
||||
if (filter != null) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Filter '" + string + "' unknown for service '" + serviceId + "'");
|
||||
}
|
||||
|
||||
private static String persistenceConfigToString(PersistenceConfig config) {
|
||||
if (config instanceof PersistenceAllConfig) {
|
||||
return "*";
|
||||
|
@ -137,6 +158,7 @@ public class PersistenceServiceConfigurationDTOMapper {
|
|||
itemDto.items = config.items().stream().map(PersistenceServiceConfigurationDTOMapper::persistenceConfigToString)
|
||||
.toList();
|
||||
itemDto.strategies = config.strategies().stream().map(PersistenceStrategy::getName).toList();
|
||||
itemDto.filters = config.filters().stream().map(PersistenceFilter::getName).toList();
|
||||
itemDto.alias = config.alias();
|
||||
return itemDto;
|
||||
}
|
||||
|
@ -153,6 +175,7 @@ public class PersistenceServiceConfigurationDTOMapper {
|
|||
filterDTO.name = thresholdFilter.getName();
|
||||
filterDTO.value = thresholdFilter.getValue();
|
||||
filterDTO.unit = thresholdFilter.getUnit();
|
||||
filterDTO.relative = thresholdFilter.isRelative();
|
||||
return filterDTO;
|
||||
}
|
||||
|
||||
|
@ -163,4 +186,22 @@ public class PersistenceServiceConfigurationDTOMapper {
|
|||
filterDTO.unit = persistenceTimeFilter.getUnit();
|
||||
return filterDTO;
|
||||
}
|
||||
|
||||
private static PersistenceFilterDTO mapPersistenceEqualsFilter(PersistenceEqualsFilter persistenceEqualsFilter) {
|
||||
PersistenceFilterDTO filterDTO = new PersistenceFilterDTO();
|
||||
filterDTO.name = persistenceEqualsFilter.getName();
|
||||
filterDTO.values = persistenceEqualsFilter.getValues().stream().toList();
|
||||
filterDTO.inverted = persistenceEqualsFilter.getInverted();
|
||||
return filterDTO;
|
||||
}
|
||||
|
||||
private static PersistenceFilterDTO mapPersistenceIncludeFilter(PersistenceIncludeFilter persistenceIncludeFilter) {
|
||||
PersistenceFilterDTO filterDTO = new PersistenceFilterDTO();
|
||||
filterDTO.name = persistenceIncludeFilter.getName();
|
||||
filterDTO.lower = persistenceIncludeFilter.getLower();
|
||||
filterDTO.upper = persistenceIncludeFilter.getUpper();
|
||||
filterDTO.unit = persistenceIncludeFilter.getUnit();
|
||||
filterDTO.inverted = persistenceIncludeFilter.getInverted();
|
||||
return filterDTO;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2023 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.core.persistence.filter;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.openhab.core.items.GenericItem;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* The {@link PersistenceEqualsFilterTest} contains tests for {@link PersistenceEqualsFilter}
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@NonNullByDefault
|
||||
public class PersistenceEqualsFilterTest {
|
||||
private static final String ITEM_NAME = "itemName";
|
||||
|
||||
private @NonNullByDefault({}) @Mock GenericItem item;
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("argumentProvider")
|
||||
public void equalsFilterTest(State state, Collection<String> values, boolean expected) {
|
||||
when(item.getState()).thenReturn(state);
|
||||
|
||||
PersistenceEqualsFilter filter = new PersistenceEqualsFilter("filter", values, false);
|
||||
assertThat(filter.apply(item), is(expected));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("argumentProvider")
|
||||
public void notEqualsFilterTest(State state, Collection<String> values, boolean expected) {
|
||||
when(item.getState()).thenReturn(state);
|
||||
|
||||
PersistenceEqualsFilter filter = new PersistenceEqualsFilter("filter", values, true);
|
||||
assertThat(filter.apply(item), is(not(expected)));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> argumentProvider() {
|
||||
return Stream.of(//
|
||||
// item state, values, result
|
||||
Arguments.of(new StringType("value1"), List.of("value1", "value2"), true),
|
||||
Arguments.of(new StringType("value3"), List.of("value1", "value2"), false),
|
||||
Arguments.of(new DecimalType(5), List.of("3", "5", "9"), true),
|
||||
Arguments.of(new DecimalType(7), List.of("3", "5", "9"), false),
|
||||
Arguments.of(new QuantityType<>(10, SIUnits.CELSIUS), List.of("5 °C", "10 °C", "15 °C"), true),
|
||||
Arguments.of(new QuantityType<>(20, SIUnits.CELSIUS), List.of("5 °C", "10 °C", "15 °C"), false),
|
||||
Arguments.of(OnOffType.ON, List.of("ON", "UNDEF", "NULL"), true),
|
||||
Arguments.of(OnOffType.OFF, List.of("ON", "UNDEF", "NULL"), false));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2023 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.core.persistence.filter;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.openhab.core.items.GenericItem;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* The {@link PersistenceIncludeFilterTest} contains tests for {@link PersistenceIncludeFilter}
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@NonNullByDefault
|
||||
public class PersistenceIncludeFilterTest {
|
||||
private static final String ITEM_NAME = "itemName";
|
||||
|
||||
private @NonNullByDefault({}) @Mock GenericItem item;
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("argumentProvider")
|
||||
public void includeFilterTest(State state, BigDecimal lower, BigDecimal upper, String unit, boolean expected) {
|
||||
when(item.getState()).thenReturn(state);
|
||||
|
||||
PersistenceIncludeFilter filter = new PersistenceIncludeFilter("filter", lower, upper, unit, false);
|
||||
assertThat(filter.apply(item), is(expected));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("notArgumentProvider")
|
||||
public void notIncludeFilterTest(State state, BigDecimal lower, BigDecimal upper, String unit, boolean expected) {
|
||||
when(item.getState()).thenReturn(state);
|
||||
|
||||
PersistenceIncludeFilter filter = new PersistenceIncludeFilter("filter", lower, upper, unit, true);
|
||||
assertThat(filter.apply(item), is(expected));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> argumentProvider() {
|
||||
return Stream.of(//
|
||||
// item state, lower, upper, unit, result
|
||||
// QuantityType
|
||||
Arguments.of(new QuantityType<>("17 °C"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "°C", true),
|
||||
Arguments.of(new QuantityType<>("17 °C"), BigDecimal.valueOf(17), BigDecimal.valueOf(19), "°C", true),
|
||||
Arguments.of(new QuantityType<>("17 °C"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "°C", true),
|
||||
Arguments.of(new QuantityType<>("10 °C"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "°C", false),
|
||||
Arguments.of(new QuantityType<>("20 °C"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "°C", false),
|
||||
Arguments.of(new QuantityType<>("0 °C"), BigDecimal.valueOf(270), BigDecimal.valueOf(275), "K", true),
|
||||
// invalid or missing units
|
||||
Arguments.of(new QuantityType<>("5 kg"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "°C", true),
|
||||
Arguments.of(new QuantityType<>("17 kg"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "°C", true),
|
||||
Arguments.of(new QuantityType<>("5 kg"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "", true),
|
||||
Arguments.of(new QuantityType<>("17 kg"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "", true),
|
||||
// DecimalType
|
||||
Arguments.of(new DecimalType("17"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "", true),
|
||||
Arguments.of(new DecimalType("17"), BigDecimal.valueOf(17), BigDecimal.valueOf(19), "", true),
|
||||
Arguments.of(new DecimalType("17"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "", true),
|
||||
Arguments.of(new DecimalType("10"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "", false),
|
||||
Arguments.of(new DecimalType("20"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "", false));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> notArgumentProvider() {
|
||||
return Stream.of(//
|
||||
// item state, lower, upper, unit, result
|
||||
// QuantityType
|
||||
Arguments.of(new QuantityType<>("17 °C"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "°C", false),
|
||||
Arguments.of(new QuantityType<>("17 °C"), BigDecimal.valueOf(17), BigDecimal.valueOf(19), "°C", true),
|
||||
Arguments.of(new QuantityType<>("17 °C"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "°C", true),
|
||||
Arguments.of(new QuantityType<>("10 °C"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "°C", true),
|
||||
Arguments.of(new QuantityType<>("20 °C"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "°C", true),
|
||||
Arguments.of(new QuantityType<>("0 °C"), BigDecimal.valueOf(270), BigDecimal.valueOf(275), "K", false),
|
||||
// invalid or missing units
|
||||
Arguments.of(new QuantityType<>("5 kg"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "°C", true),
|
||||
Arguments.of(new QuantityType<>("17 kg"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "°C", true),
|
||||
Arguments.of(new QuantityType<>("5 kg"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "", true),
|
||||
Arguments.of(new QuantityType<>("17 kg"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "", true),
|
||||
// DecimalType
|
||||
Arguments.of(new DecimalType("17"), BigDecimal.valueOf(14), BigDecimal.valueOf(19), "", false),
|
||||
Arguments.of(new DecimalType("17"), BigDecimal.valueOf(17), BigDecimal.valueOf(19), "", true),
|
||||
Arguments.of(new DecimalType("17"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "", true),
|
||||
Arguments.of(new DecimalType("10"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "", true),
|
||||
Arguments.of(new DecimalType("20"), BigDecimal.valueOf(14), BigDecimal.valueOf(17), "", true));
|
||||
}
|
||||
}
|
|
@ -70,42 +70,43 @@ public class PersistenceThresholdFilterTest {
|
|||
|
||||
@Test
|
||||
public void differentItemSameValue() {
|
||||
filterTest(ITEM_NAME_2, DecimalType.ZERO, DecimalType.ZERO, "", true);
|
||||
filterTest(ITEM_NAME_2, DecimalType.ZERO, DecimalType.ZERO, "", false, true);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("argumentProvider")
|
||||
public void filterTest(State state1, State state2, String unit, boolean expected) {
|
||||
filterTest(ITEM_NAME_1, state1, state2, unit, expected);
|
||||
public void filterTest(State state1, State state2, String unit, boolean relative, boolean expected) {
|
||||
filterTest(ITEM_NAME_1, state1, state2, unit, relative, expected);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> argumentProvider() {
|
||||
return Stream.of(//
|
||||
// same item, same value -> false
|
||||
Arguments.of(DecimalType.ZERO, DecimalType.ZERO, "", false),
|
||||
Arguments.of(DecimalType.ZERO, DecimalType.ZERO, "", false, false),
|
||||
// plain decimal, below threshold, absolute
|
||||
Arguments.of(DecimalType.ZERO, DecimalType.valueOf("5"), "", false),
|
||||
Arguments.of(DecimalType.ZERO, DecimalType.valueOf("5"), "", false, false),
|
||||
// plain decimal, above threshold, absolute
|
||||
Arguments.of(DecimalType.ZERO, DecimalType.valueOf("15"), "", true),
|
||||
Arguments.of(DecimalType.ZERO, DecimalType.valueOf("15"), "", false, true),
|
||||
// plain decimal, below threshold, relative
|
||||
Arguments.of(DecimalType.valueOf("10.0"), DecimalType.valueOf("9.5"), "%", false),
|
||||
Arguments.of(DecimalType.valueOf("10.0"), DecimalType.valueOf("9.5"), "", true, false),
|
||||
// plain decimal, above threshold, relative
|
||||
Arguments.of(DecimalType.valueOf("10.0"), DecimalType.valueOf("11.5"), "%", true),
|
||||
Arguments.of(DecimalType.valueOf("10.0"), DecimalType.valueOf("11.5"), "", true, true),
|
||||
// quantity type, below threshold, relative
|
||||
Arguments.of(new QuantityType<>("15 A"), new QuantityType<>("14000 mA"), "%", false),
|
||||
Arguments.of(new QuantityType<>("15 A"), new QuantityType<>("14000 mA"), "", true, false),
|
||||
// quantity type, above threshold, relative
|
||||
Arguments.of(new QuantityType<>("2000 mbar"), new QuantityType<>("2.6 bar"), "%", true),
|
||||
Arguments.of(new QuantityType<>("2000 mbar"), new QuantityType<>("2.6 bar"), "", true, true),
|
||||
// quantity type, below threshold, absolute, no unit
|
||||
Arguments.of(new QuantityType<>("100 K"), new QuantityType<>("105 K"), "", false),
|
||||
Arguments.of(new QuantityType<>("100 K"), new QuantityType<>("105 K"), "", false, false),
|
||||
// quantity type, above threshold, absolute, no unit
|
||||
Arguments.of(new QuantityType<>("20 V"), new QuantityType<>("9000 mV"), "", true),
|
||||
Arguments.of(new QuantityType<>("20 V"), new QuantityType<>("9000 mV"), "", false, true),
|
||||
// quantity type, below threshold, absolute, with unit
|
||||
Arguments.of(new QuantityType<>("10 m"), new QuantityType<>("10.002 m"), "mm", false),
|
||||
Arguments.of(new QuantityType<>("10 m"), new QuantityType<>("10.002 m"), "mm", false, false),
|
||||
// quantity type, above threshold, absolute, with unit
|
||||
Arguments.of(new QuantityType<>("-10 °C"), new QuantityType<>("5 °C"), "K", true));
|
||||
Arguments.of(new QuantityType<>("-10 °C"), new QuantityType<>("5 °C"), "K", false, true));
|
||||
}
|
||||
|
||||
private void filterTest(String item2name, State state1, State state2, String unit, boolean expected) {
|
||||
private void filterTest(String item2name, State state1, State state2, String unit, boolean relative,
|
||||
boolean expected) {
|
||||
String itemType = "Number";
|
||||
if (state1 instanceof QuantityType<?> q) {
|
||||
itemType += ":" + UnitUtils.getDimensionName(q.getUnit());
|
||||
|
@ -117,7 +118,7 @@ public class PersistenceThresholdFilterTest {
|
|||
item1.setState(state1);
|
||||
item2.setState(state2);
|
||||
|
||||
PersistenceFilter filter = new PersistenceThresholdFilter("test", BigDecimal.TEN, unit);
|
||||
PersistenceFilter filter = new PersistenceThresholdFilter("test", BigDecimal.TEN, unit, relative);
|
||||
|
||||
assertThat(filter.apply(item1), is(true));
|
||||
filter.persisted(item1);
|
||||
|
|
|
@ -308,7 +308,7 @@ public class PersistenceManagerTest {
|
|||
new PersistenceCronStrategy("withoutFilter", "0 0 * * * ?"), null);
|
||||
addConfiguration(TEST_QUERYABLE_PERSISTENCE_SERVICE_ID, new PersistenceItemConfig(TEST_ITEM3_NAME),
|
||||
new PersistenceCronStrategy("withFilter", "0 * * * * ?"),
|
||||
new PersistenceThresholdFilter("test", BigDecimal.TEN, ""));
|
||||
new PersistenceThresholdFilter("test", BigDecimal.TEN, "", false));
|
||||
|
||||
manager.onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
|
||||
|
@ -352,7 +352,7 @@ public class PersistenceManagerTest {
|
|||
@Test
|
||||
public void filterAppliesOnStateUpdate() {
|
||||
addConfiguration(TEST_PERSISTENCE_SERVICE_ID, new PersistenceAllConfig(), PersistenceStrategy.Globals.UPDATE,
|
||||
new PersistenceThresholdFilter("test", BigDecimal.TEN, ""));
|
||||
new PersistenceThresholdFilter("test", BigDecimal.TEN, "", false));
|
||||
|
||||
manager.stateUpdated(TEST_ITEM3, DecimalType.ZERO);
|
||||
manager.stateUpdated(TEST_ITEM3, DecimalType.ZERO);
|
||||
|
|
Loading…
Reference in New Issue