Skip to content

Commit

Permalink
Improve existing and add new persistence filters (#3642)
Browse files Browse the repository at this point in the history
* Improve existing and add new persistence filters
* include filter

Signed-off-by: Jan N. Klug <github@klug.nrw>
  • Loading branch information
J-N-K authored Jun 18, 2023
1 parent c2e81a1 commit d64bd3b
Show file tree
Hide file tree
Showing 13 changed files with 503 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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])*)?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@
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;
Expand All @@ -40,7 +44,9 @@
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;
Expand Down Expand Up @@ -175,13 +181,21 @@ private List<PersistenceFilter> mapFilters(List<Filter> filters) {
}

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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -21,7 +22,24 @@
* @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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

/**
* 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}.
*
Expand All @@ -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() {
Expand All @@ -60,6 +62,10 @@ public String getUnit() {
return unit;
}

public boolean isRelative() {
return relative;
}

@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public boolean apply(Item item) {
Expand All @@ -78,15 +84,15 @@ public boolean apply(Item item) {
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;
} else {
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;
Expand Down Expand Up @@ -117,6 +123,7 @@ public void persisted(Item item) {

@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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

/**
* 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
Expand Down
Loading

0 comments on commit d64bd3b

Please sign in to comment.