From d11610782812812ffa92533cce84b153b88b22ff Mon Sep 17 00:00:00 2001 From: Jimmy Tanagra Date: Sat, 11 Jan 2025 19:17:21 +1000 Subject: [PATCH] [basicprofiles] Fix inability to filter a percent QuantityType input Signed-off-by: Jimmy Tanagra --- .../org.openhab.transform.basicprofiles/README.md | 5 +++++ .../internal/profiles/StateFilterProfile.java | 2 ++ .../internal/profiles/StateFilterProfileTest.java | 12 ++++++++++++ 3 files changed, 19 insertions(+) diff --git a/bundles/org.openhab.transform.basicprofiles/README.md b/bundles/org.openhab.transform.basicprofiles/README.md index ac6db7b2787cf..1687b35fe7167 100644 --- a/bundles/org.openhab.transform.basicprofiles/README.md +++ b/bundles/org.openhab.transform.basicprofiles/README.md @@ -231,11 +231,16 @@ The `LHS_OPERAND` and the `RHS_OPERAND` can be either one of these: It is calculated as `($DELTA / current_data) * 100`. Note that this can also be done by omitting the `LHS_OPERAND` and using a number followed with a percent sign `%` as the `RHS_OPERAND`. See the example below. + - `$INPUT` to represent the incoming value. + Conditions that compare QuantityType inputs to a constant must include the relevant unit (e.g. `> 100 W`). + However, when the unit is `%` (e.g. `> 10 %`), the condition will be misinterpreted as a `$DELTA_PERCENT` check. + To avoid this, write the condition as `$INPUT > 10 %`. - `$AVERAGE`, or `$AVG` to represent the average of the previous unfiltered incoming values. - `$STDDEV` to represent the _population_ standard deviation of the previous unfiltered incoming values. - `$MEDIAN` to represent the median value of the previous unfiltered incoming values. - `$MIN` to represent the minimum value of the previous unfiltered incoming values. - `$MAX` to represent the maximum value of the previous unfiltered incoming values. + These are only applicable to numeric states. By default, 5 samples of the previous values are kept. This can be customized by specifying the "window size" or sample count applicable to the function, e.g. `$MEDIAN(10)` will return the median of the last 10 values. diff --git a/bundles/org.openhab.transform.basicprofiles/src/main/java/org/openhab/transform/basicprofiles/internal/profiles/StateFilterProfile.java b/bundles/org.openhab.transform.basicprofiles/src/main/java/org/openhab/transform/basicprofiles/internal/profiles/StateFilterProfile.java index c8f88dc751176..6385af11764d7 100644 --- a/bundles/org.openhab.transform.basicprofiles/src/main/java/org/openhab/transform/basicprofiles/internal/profiles/StateFilterProfile.java +++ b/bundles/org.openhab.transform.basicprofiles/src/main/java/org/openhab/transform/basicprofiles/internal/profiles/StateFilterProfile.java @@ -511,6 +511,7 @@ public String toString() { */ class FunctionType implements State { enum Function { + INPUT, DELTA, DELTA_PERCENT, AVERAGE, @@ -535,6 +536,7 @@ public FunctionType(Function type, Optional windowSize) { int start = windowSize.map(w -> size - w).orElse(0); List states = start <= 0 ? previousStates : previousStates.subList(start, size); return switch (type) { + case INPUT -> newState; case DELTA -> calculateDelta(); case DELTA_PERCENT -> calculateDeltaPercent(); case AVG, AVERAGE -> calculateAverage(states); diff --git a/bundles/org.openhab.transform.basicprofiles/src/test/java/org/openhab/transform/basicprofiles/internal/profiles/StateFilterProfileTest.java b/bundles/org.openhab.transform.basicprofiles/src/test/java/org/openhab/transform/basicprofiles/internal/profiles/StateFilterProfileTest.java index 51db627d46b6b..b46c609326392 100644 --- a/bundles/org.openhab.transform.basicprofiles/src/test/java/org/openhab/transform/basicprofiles/internal/profiles/StateFilterProfileTest.java +++ b/bundles/org.openhab.transform.basicprofiles/src/test/java/org/openhab/transform/basicprofiles/internal/profiles/StateFilterProfileTest.java @@ -26,8 +26,11 @@ import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Stream; +import javax.measure.Unit; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -67,6 +70,7 @@ import org.openhab.core.thing.profiles.ProfileContext; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; +import org.openhab.core.types.util.UnitUtils; import org.osgi.framework.BundleContext; import org.osgi.service.component.ComponentContext; @@ -657,14 +661,22 @@ public void testComparingInputStateWithItem(GenericItem linkedItem, State inputS public static Stream testFunctions() { NumberItem powerItem = new NumberItem("Number:Power", "powerItem", UNIT_PROVIDER); + NumberItem percentItem = new NumberItem("Number:Dimensionless", "percentItem", UNIT_PROVIDER); NumberItem decimalItem = new NumberItem("decimalItem"); List numbers = List.of(1, 2, 3, 4, 5); List negatives = List.of(-1, -2, -3, -4, -5); List quantities = numbers.stream().map(n -> new QuantityType(n, Units.WATT)).toList(); + Unit percentUnit = Objects.requireNonNull(UnitUtils.parseUnit("%")); + List percents = numbers.stream().map(n -> new QuantityType(n, percentUnit)).toList(); List decimals = numbers.stream().map(DecimalType::new).toList(); List negativeDecimals = negatives.stream().map(DecimalType::new).toList(); return Stream.of( // + Arguments.of(decimalItem, "$INPUT < 10", decimals, DecimalType.valueOf("3"), true), // + Arguments.of(decimalItem, "$INPUT < 10", decimals, DecimalType.valueOf("10"), false), // + Arguments.of(percentItem, "$INPUT < 10 %", percents, QuantityType.valueOf("-10 %"), true), // + Arguments.of(percentItem, "$INPUT < 10 %", percents, QuantityType.valueOf("10 %"), false), // + // test custom window size Arguments.of(decimalItem, "$AVERAGE(3) == 4", decimals, DecimalType.valueOf("5"), true), // Arguments.of(decimalItem, "$AVERAGE(4) == 3.5", decimals, DecimalType.valueOf("5"), true), //