From d800e82f25ce807127aef6492bfffb932ee6ef37 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 18 Jan 2025 19:49:19 +0000 Subject: [PATCH 01/16] QuantityType improve group calculations Signed-off-by: Andrew Fiddian-Green --- .../internal/items/GroupFunctionHelper.java | 8 +- .../QuantityTypeArithmeticGroupFunction.java | 157 ++++++++++-------- ...antityTypeArithmeticGroupFunctionTest.java | 30 ++-- 3 files changed, 111 insertions(+), 84 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java index 47c39bf7107..58ac18101d4 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java @@ -90,15 +90,15 @@ private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, @N final String functionName = function.name; switch (functionName.toUpperCase()) { case "AVG": - return new QuantityTypeArithmeticGroupFunction.Avg(dimension); + return new QuantityTypeArithmeticGroupFunction.Avg(dimension, baseItem); case "MEDIAN": return new QuantityTypeArithmeticGroupFunction.Median(dimension, baseItem); case "SUM": - return new QuantityTypeArithmeticGroupFunction.Sum(dimension); + return new QuantityTypeArithmeticGroupFunction.Sum(dimension, baseItem); case "MIN": - return new QuantityTypeArithmeticGroupFunction.Min(dimension); + return new QuantityTypeArithmeticGroupFunction.Min(dimension, baseItem); case "MAX": - return new QuantityTypeArithmeticGroupFunction.Max(dimension); + return new QuantityTypeArithmeticGroupFunction.Max(dimension, baseItem); default: return createDefaultGroupFunction(function, baseItem); } diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 69f78dbc2bf..880fbe8059b 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -27,6 +27,7 @@ import org.openhab.core.items.GroupItem; import org.openhab.core.items.Item; import org.openhab.core.library.items.NumberItem; +import org.openhab.core.library.unit.Units; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; import org.openhab.core.util.Statistics; @@ -42,9 +43,11 @@ public interface QuantityTypeArithmeticGroupFunction extends GroupFunction { abstract class DimensionalGroupFunction implements GroupFunction { protected final Class> dimension; + protected final @Nullable Item baseItem; - public DimensionalGroupFunction(Class> dimension) { + public DimensionalGroupFunction(Class> dimension, @Nullable Item baseItem) { this.dimension = dimension; + this.baseItem = baseItem; } @Override @@ -68,6 +71,14 @@ protected boolean isSameDimension(@Nullable Item item) { } return item instanceof NumberItem ni && dimension.equals(ni.getDimension()); } + + protected Unit getGroupItemUnitOrDefault(Item defaultSourceItem) { + Unit unit = (baseItem instanceof NumberItem numberItem) ? numberItem.getUnit() : null; + if (unit == null) { + unit = (defaultSourceItem instanceof NumberItem numberItem) ? numberItem.getUnit() : null; + } + return unit != null ? unit : Units.ONE; + } } /** @@ -75,8 +86,8 @@ protected boolean isSameDimension(@Nullable Item item) { */ class Avg extends DimensionalGroupFunction { - public Avg(Class> dimension) { - super(dimension); + public Avg(Class> dimension, @Nullable Item baseItem) { + super(dimension, baseItem); } @Override @@ -86,23 +97,27 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } + Unit baseUnit = null; QuantityType sum = null; int count = 0; for (Item item : items) { - if (isSameDimension(item)) { - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState != null) { - if (sum == null) { - sum = itemState; // initialise the sum from the first item - count++; - } else { - itemState = itemState.toInvertibleUnit(sum.getUnit()); - if (itemState != null) { - sum = sum.add(itemState); - count++; - } - } - } + if (!isSameDimension(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (baseUnit == null) { + baseUnit = getGroupItemUnitOrDefault(item); + } + if (sum == null) { + sum = QuantityType.valueOf(0, baseUnit); + } + itemState = itemState.toInvertibleUnit(baseUnit); + if (itemState != null) { + sum = sum.add(itemState); + count++; } } @@ -120,11 +135,8 @@ public State calculate(@Nullable Set items) { */ class Median extends DimensionalGroupFunction { - private @Nullable Item baseItem; - public Median(Class> dimension, @Nullable Item baseItem) { - super(dimension); - this.baseItem = baseItem; + super(dimension, baseItem); } @Override @@ -132,10 +144,8 @@ public Median(Class> dimension, @Nullable Item baseItem) { public State calculate(@Nullable Set items) { if (items != null) { List values = new ArrayList<>(); - Unit unit = null; - if (baseItem instanceof NumberItem numberItem) { - unit = numberItem.getUnit(); - } + + Unit baseUnit = null; for (Item item : items) { if (!isSameDimension(item)) { continue; @@ -144,18 +154,18 @@ public State calculate(@Nullable Set items) { if (itemState == null) { continue; } - if (unit == null) { - unit = itemState.getUnit(); // set it to the first item's unit + if (baseUnit == null) { + baseUnit = getGroupItemUnitOrDefault(item); } - if (itemState.toInvertibleUnit(unit) instanceof QuantityType inverted) { - values.add(inverted.toBigDecimal()); + if (itemState.toInvertibleUnit(baseUnit) instanceof QuantityType value) { + values.add(value.toBigDecimal()); } } if (!values.isEmpty()) { BigDecimal median = Statistics.median(values); - if (median != null && unit != null) { - return new QuantityType<>(median, unit); + if (median != null && baseUnit != null) { + return new QuantityType<>(median, baseUnit); } } @@ -169,8 +179,8 @@ public State calculate(@Nullable Set items) { */ class Sum extends DimensionalGroupFunction { - public Sum(Class> dimension) { - super(dimension); + public Sum(Class> dimension, @Nullable Item baseItem) { + super(dimension, baseItem); } @Override @@ -180,20 +190,25 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } + Unit baseUnit = null; QuantityType sum = null; for (Item item : items) { - if (isSameDimension(item)) { - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState != null) { - if (sum == null) { - sum = itemState; // initialise the sum from the first item - } else { - itemState = itemState.toUnit(sum.getUnit()); - if (itemState != null) { - sum = sum.add(itemState); - } - } - } + if (!isSameDimension(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (baseUnit == null) { + baseUnit = getGroupItemUnitOrDefault(item); + } + if (sum == null) { + sum = QuantityType.valueOf(0, baseUnit); + } + itemState = itemState.toInvertibleUnit(baseUnit); + if (itemState != null) { + sum = sum.add(itemState); } } @@ -206,8 +221,8 @@ public State calculate(@Nullable Set items) { */ class Min extends DimensionalGroupFunction { - public Min(Class> dimension) { - super(dimension); + public Min(Class> dimension, @Nullable Item baseItem) { + super(dimension, baseItem); } @Override @@ -217,16 +232,22 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } + Unit baseUnit = null; QuantityType min = null; for (Item item : items) { - if (isSameDimension(item)) { - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState != null) { - if (min == null - || (min.getUnit().isCompatible(itemState.getUnit()) && min.compareTo(itemState) > 0)) { - min = itemState; - } - } + if (!isSameDimension(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (baseUnit == null) { + baseUnit = getGroupItemUnitOrDefault(item); + } + itemState = itemState.toInvertibleUnit(baseUnit); + if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { + min = itemState; } } @@ -239,8 +260,8 @@ public State calculate(@Nullable Set items) { */ class Max extends DimensionalGroupFunction { - public Max(Class> dimension) { - super(dimension); + public Max(Class> dimension, @Nullable Item baseItem) { + super(dimension, baseItem); } @Override @@ -250,16 +271,22 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } + Unit baseUnit = null; QuantityType max = null; for (Item item : items) { - if (isSameDimension(item)) { - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState != null) { - if (max == null - || (max.getUnit().isCompatible(itemState.getUnit()) && max.compareTo(itemState) < 0)) { - max = itemState; - } - } + if (!isSameDimension(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (baseUnit == null) { + baseUnit = getGroupItemUnitOrDefault(item); + } + itemState = itemState.toInvertibleUnit(baseUnit); + if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { + max = itemState; } } diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java index 03031323520..d840b511208 100644 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java @@ -12,7 +12,7 @@ */ package org.openhab.core.library.types; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.number.IsCloseTo.closeTo; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -85,7 +85,7 @@ public void testSumFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("122.41 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("234.95 °C"), state); @@ -103,7 +103,7 @@ public void testSumFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("234.95 °C"), state); @@ -119,7 +119,7 @@ public void testSumFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -137,7 +137,7 @@ public void testAvgFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("200 °C"), state); @@ -163,7 +163,7 @@ public void testAvgFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("55.33333333333333333333333333333334 °C"), state); @@ -179,7 +179,7 @@ public void testAvgFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -242,7 +242,7 @@ public void testMaxFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("300 °C"), state); @@ -260,7 +260,7 @@ public void testMaxFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("100 °C"), state); @@ -276,7 +276,7 @@ public void testMaxFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -294,7 +294,7 @@ public void testMinFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("100 °C"), state); @@ -313,7 +313,7 @@ public void testMaxFunctionQuantityTypeOnDimensionless(Locale locale) { items.add(createNumberItem("TestItem5", Dimensionless.class, new QuantityType<>("0 %"))); items.add(createNumberItem("TestItem6", Dimensionless.class, new QuantityType<>("0 %"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Dimensionless.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Dimensionless.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("48 %"), state); @@ -331,7 +331,7 @@ public void testMinFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("294.15 K"), state); @@ -347,7 +347,7 @@ public void testMinFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -362,7 +362,7 @@ public void testSumFunctionQuantityTypeWithGroups(Locale locale) { items.add(createNumberItem("TestItem1", Power.class, new QuantityType<>("5 W"))); items.add(createGroupItem("TestGroup1", Power.class, new QuantityType<>("5 W"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Power.class); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Power.class, null); State state = function.calculate(items); assertEquals(new QuantityType<>("10 W"), state); From e4e45aaea7408fcb6e1cf2257ca74dc62ba53718 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 19 Jan 2025 13:25:38 +0000 Subject: [PATCH 02/16] support inverse units, extend tests Signed-off-by: Andrew Fiddian-Green --- .../QuantityTypeArithmeticGroupFunction.java | 83 ++++----- .../internal/i18n/TestKelvinProvider.java | 50 ++++++ ...antityTypeArithmeticGroupFunctionTest.java | 161 ++++++++++++++++++ 3 files changed, 256 insertions(+), 38 deletions(-) create mode 100644 bundles/org.openhab.core/src/test/java/org/openhab/core/internal/i18n/TestKelvinProvider.java diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 880fbe8059b..b68325e3827 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -65,17 +65,24 @@ public State[] getParameters() { return new State[0]; } - protected boolean isSameDimension(@Nullable Item item) { - if (item instanceof GroupItem groupItem) { - return isSameDimension(groupItem.getBaseItem()); + protected boolean isCompatible(@Nullable Item member) { + if (member instanceof GroupItem groupItem) { + return isCompatible(groupItem.getBaseItem()); } - return item instanceof NumberItem ni && dimension.equals(ni.getDimension()); + if ((baseItem instanceof NumberItem baseNI) && (member instanceof NumberItem memberNI)) { + Unit> baseUnit = baseNI.getUnit(); + Unit> memberUnit = memberNI.getUnit(); + if (baseUnit != null && memberUnit != null) { + return baseUnit.isCompatible(memberUnit) || baseUnit.isCompatible(memberUnit.inverse()); + } + } + return member instanceof NumberItem memberNI && dimension.equals(memberNI.getDimension()); } - protected Unit getGroupItemUnitOrDefault(Item defaultSourceItem) { - Unit unit = (baseItem instanceof NumberItem numberItem) ? numberItem.getUnit() : null; + protected Unit getBaseUnitOrDefault(Item defaultItem) { + Unit unit = baseItem instanceof NumberItem bi ? bi.getUnit() : null; if (unit == null) { - unit = (defaultSourceItem instanceof NumberItem numberItem) ? numberItem.getUnit() : null; + unit = defaultItem instanceof NumberItem di ? di.getUnit() : null; } return unit != null ? unit : Units.ONE; } @@ -97,24 +104,24 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } - Unit baseUnit = null; + Unit unit = null; QuantityType sum = null; int count = 0; for (Item item : items) { - if (!isSameDimension(item)) { + if (!isCompatible(item)) { continue; } QuantityType itemState = item.getStateAs(QuantityType.class); if (itemState == null) { continue; } - if (baseUnit == null) { - baseUnit = getGroupItemUnitOrDefault(item); + if (unit == null) { + unit = getBaseUnitOrDefault(item); } if (sum == null) { - sum = QuantityType.valueOf(0, baseUnit); + sum = QuantityType.valueOf(0, unit); } - itemState = itemState.toInvertibleUnit(baseUnit); + itemState = itemState.toInvertibleUnit(unit); if (itemState != null) { sum = sum.add(itemState); count++; @@ -145,27 +152,27 @@ public State calculate(@Nullable Set items) { if (items != null) { List values = new ArrayList<>(); - Unit baseUnit = null; + Unit unit = null; for (Item item : items) { - if (!isSameDimension(item)) { + if (!isCompatible(item)) { continue; } QuantityType itemState = item.getStateAs(QuantityType.class); if (itemState == null) { continue; } - if (baseUnit == null) { - baseUnit = getGroupItemUnitOrDefault(item); + if (unit == null) { + unit = getBaseUnitOrDefault(item); } - if (itemState.toInvertibleUnit(baseUnit) instanceof QuantityType value) { + if (itemState.toInvertibleUnit(unit) instanceof QuantityType value) { values.add(value.toBigDecimal()); } } if (!values.isEmpty()) { BigDecimal median = Statistics.median(values); - if (median != null && baseUnit != null) { - return new QuantityType<>(median, baseUnit); + if (median != null && unit != null) { + return new QuantityType<>(median, unit); } } @@ -190,23 +197,23 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } - Unit baseUnit = null; + Unit unit = null; QuantityType sum = null; for (Item item : items) { - if (!isSameDimension(item)) { + if (!isCompatible(item)) { continue; } QuantityType itemState = item.getStateAs(QuantityType.class); if (itemState == null) { continue; } - if (baseUnit == null) { - baseUnit = getGroupItemUnitOrDefault(item); + if (unit == null) { + unit = getBaseUnitOrDefault(item); } if (sum == null) { - sum = QuantityType.valueOf(0, baseUnit); + sum = QuantityType.valueOf(0, unit); } - itemState = itemState.toInvertibleUnit(baseUnit); + itemState = itemState.toInvertibleUnit(unit); if (itemState != null) { sum = sum.add(itemState); } @@ -232,20 +239,20 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } - Unit baseUnit = null; + Unit unit = null; QuantityType min = null; for (Item item : items) { - if (!isSameDimension(item)) { + if (!isCompatible(item)) { continue; } QuantityType itemState = item.getStateAs(QuantityType.class); if (itemState == null) { continue; } - if (baseUnit == null) { - baseUnit = getGroupItemUnitOrDefault(item); + if (unit == null) { + unit = getBaseUnitOrDefault(item); } - itemState = itemState.toInvertibleUnit(baseUnit); + itemState = itemState.toInvertibleUnit(unit); if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { min = itemState; } @@ -260,8 +267,8 @@ public State calculate(@Nullable Set items) { */ class Max extends DimensionalGroupFunction { - public Max(Class> dimension, @Nullable Item baseItem) { - super(dimension, baseItem); + public Max(Class> dimension, @Nullable Item groupItem) { + super(dimension, groupItem); } @Override @@ -271,20 +278,20 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } - Unit baseUnit = null; + Unit unit = null; QuantityType max = null; for (Item item : items) { - if (!isSameDimension(item)) { + if (!isCompatible(item)) { continue; } QuantityType itemState = item.getStateAs(QuantityType.class); if (itemState == null) { continue; } - if (baseUnit == null) { - baseUnit = getGroupItemUnitOrDefault(item); + if (unit == null) { + unit = getBaseUnitOrDefault(item); } - itemState = itemState.toInvertibleUnit(baseUnit); + itemState = itemState.toInvertibleUnit(unit); if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { max = itemState; } diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/internal/i18n/TestKelvinProvider.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/internal/i18n/TestKelvinProvider.java new file mode 100644 index 00000000000..1760969bd64 --- /dev/null +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/internal/i18n/TestKelvinProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010-2025 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.internal.i18n; + +import java.util.Collection; +import java.util.Set; + +import javax.measure.Quantity; +import javax.measure.Unit; +import javax.measure.spi.SystemOfUnits; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.i18n.UnitProvider; +import org.openhab.core.library.unit.Units; + +/** + * The {@link TestKelvinProvider} implements a {@link UnitProvider} for testing purposes + * that only returns {@link Units.KELVIN} + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class TestKelvinProvider implements UnitProvider { + + @Override + @SuppressWarnings("unchecked") + public > Unit getUnit(Class dimension) { + return (Unit) Units.KELVIN; + } + + @Override + public SystemOfUnits getMeasurementSystem() { + return Units.getInstance(); + } + + @Override + public Collection>> getAllDimensions() { + return Set.of(); + } +} diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java index d840b511208..7eb4b7e29a9 100644 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java @@ -40,12 +40,16 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.openhab.core.i18n.UnitProvider; +import org.openhab.core.internal.i18n.TestKelvinProvider; import org.openhab.core.internal.i18n.TestUnitProvider; import org.openhab.core.items.GroupFunction; import org.openhab.core.items.GroupItem; import org.openhab.core.items.Item; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.items.NumberItem; +import org.openhab.core.library.unit.ImperialUnits; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; import org.osgi.service.component.ComponentContext; @@ -59,6 +63,7 @@ public class QuantityTypeArithmeticGroupFunctionTest { private @Mock @NonNullByDefault({}) ComponentContext componentContext; private final UnitProvider unitProvider = new TestUnitProvider(); + private final UnitProvider kelvinProvider = new TestKelvinProvider(); /** * Locales having a different decimal and grouping separators to test string parsing and generation. @@ -380,4 +385,160 @@ private GroupItem createGroupItem(String name, Class> dime item.setState(state); return item; } + + @ParameterizedTest + @MethodSource("locales") + public void testSumFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C"))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("192.2 °F"))); + items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); + items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K"))); + + Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, baseItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("1054.40 K"), state); + } + + @ParameterizedTest + @MethodSource("locales") + public void testAvgFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C"))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F"))); + items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); + items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); + + Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, baseItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("55.33333333333333333333333333333334 °C"), state); + } + + @ParameterizedTest + @MethodSource("locales") + public void testMaxFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C"))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F"))); + items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); + items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); + + Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, baseItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("100 °C"), state); + } + + @ParameterizedTest + @MethodSource("locales") + public void testMinFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C"))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F"))); + items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); + items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); + + Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, baseItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("294.15 K"), state); + } + + @ParameterizedTest + @MethodSource("locales") + public void testSumFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2000, Units.KELVIN))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS))); + items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED))); + items.add(createNumberItem("TestItem5", Temperature.class, + QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); + + Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, baseItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("8000 K"), state); + } + + @ParameterizedTest + @MethodSource("locales") + public void testAvgFunctionQuantityTypeColorTempDifferentUnitsWithBaseItem(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2000, Units.KELVIN))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS))); + items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED))); + items.add(createNumberItem("TestItem5", Temperature.class, + QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); + + Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, baseItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("2000 K"), state); + } + + @ParameterizedTest + @MethodSource("locales") + public void testMinFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(1999, Units.KELVIN))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS))); + items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED))); + items.add(createNumberItem("TestItem5", Temperature.class, + QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); + + Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, baseItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("1999 K"), state); + } + + @ParameterizedTest + @MethodSource("locales") + public void testMaxFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2001, Units.KELVIN))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS))); + items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED))); + items.add(createNumberItem("TestItem5", Temperature.class, + QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); + + Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, baseItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("2001 K"), state); + } } From d5b5072984e584f591312dde2a38c0b60ed81c68 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 19 Jan 2025 13:38:37 +0000 Subject: [PATCH 03/16] refactoring Signed-off-by: Andrew Fiddian-Green --- .../library/types/QuantityTypeArithmeticGroupFunction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index b68325e3827..59b10ba3b2a 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -80,9 +80,9 @@ protected boolean isCompatible(@Nullable Item member) { } protected Unit getBaseUnitOrDefault(Item defaultItem) { - Unit unit = baseItem instanceof NumberItem bi ? bi.getUnit() : null; + Unit unit = baseItem instanceof NumberItem baseNI ? baseNI.getUnit() : null; if (unit == null) { - unit = defaultItem instanceof NumberItem di ? di.getUnit() : null; + unit = defaultItem instanceof NumberItem defaultNI ? defaultNI.getUnit() : null; } return unit != null ? unit : Units.ONE; } From 4a13e1ea9697f865d298ac6ab07a08773decd3cf Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 19 Jan 2025 20:14:20 +0000 Subject: [PATCH 04/16] Adopt (some) reviewer suggestions Signed-off-by: Andrew Fiddian-Green --- .../internal/items/GroupFunctionHelper.java | 29 +-- .../QuantityTypeArithmeticGroupFunction.java | 210 +++++++++--------- ...antityTypeArithmeticGroupFunctionTest.java | 136 ++++++++---- .../types}/TestKelvinProvider.java | 2 +- .../core/library/types/TestMirekProvider.java | 50 +++++ 5 files changed, 252 insertions(+), 175 deletions(-) rename bundles/org.openhab.core/src/test/java/org/openhab/core/{internal/i18n => library/types}/TestKelvinProvider.java (97%) create mode 100644 bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestMirekProvider.java diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java index 58ac18101d4..85df5ab4956 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java @@ -15,8 +15,6 @@ import java.util.ArrayList; import java.util.List; -import javax.measure.Quantity; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.items.GroupFunction; @@ -48,13 +46,12 @@ public class GroupFunctionHelper { * arithmetic group function will take unit conversion into account. * * @param function the {@link GroupFunctionDTO} describing the group function. - * @param baseItem an optional {@link Item} defining the dimension for unit conversion. + * @param baseItem an optional {@link Item} defining the dimension/unit for unit conversion. * @return a {@link GroupFunction} according to the given parameters. */ public GroupFunction createGroupFunction(GroupFunctionDTO function, @Nullable Item baseItem) { - Class> dimension = getDimension(baseItem); - if (dimension != null) { - return createDimensionGroupFunction(function, baseItem, dimension); + if (baseItem instanceof NumberItem baseNumberItem && baseNumberItem.getDimension() != null) { + return createDimensionGroupFunction(function, baseNumberItem); } return createDefaultGroupFunction(function, baseItem); } @@ -78,27 +75,19 @@ private List parseStates(@Nullable Item baseItem, String @Nullable [] par return states; } - private @Nullable Class> getDimension(@Nullable Item baseItem) { - if (baseItem instanceof NumberItem numberItem) { - return numberItem.getDimension(); - } - return null; - } - - private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, @Nullable Item baseItem, - Class> dimension) { + private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, NumberItem baseItem) { final String functionName = function.name; switch (functionName.toUpperCase()) { case "AVG": - return new QuantityTypeArithmeticGroupFunction.Avg(dimension, baseItem); + return new QuantityTypeArithmeticGroupFunction.Avg(baseItem); case "MEDIAN": - return new QuantityTypeArithmeticGroupFunction.Median(dimension, baseItem); + return new QuantityTypeArithmeticGroupFunction.Median(baseItem); case "SUM": - return new QuantityTypeArithmeticGroupFunction.Sum(dimension, baseItem); + return new QuantityTypeArithmeticGroupFunction.Sum(baseItem); case "MIN": - return new QuantityTypeArithmeticGroupFunction.Min(dimension, baseItem); + return new QuantityTypeArithmeticGroupFunction.Min(baseItem); case "MAX": - return new QuantityTypeArithmeticGroupFunction.Max(dimension, baseItem); + return new QuantityTypeArithmeticGroupFunction.Max(baseItem); default: return createDefaultGroupFunction(function, baseItem); } diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 59b10ba3b2a..7134fc41c33 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -27,7 +27,6 @@ import org.openhab.core.items.GroupItem; import org.openhab.core.items.Item; import org.openhab.core.library.items.NumberItem; -import org.openhab.core.library.unit.Units; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; import org.openhab.core.util.Statistics; @@ -42,12 +41,10 @@ public interface QuantityTypeArithmeticGroupFunction extends GroupFunction { abstract class DimensionalGroupFunction implements GroupFunction { - protected final Class> dimension; - protected final @Nullable Item baseItem; + protected final NumberItem baseNumberItem; - public DimensionalGroupFunction(Class> dimension, @Nullable Item baseItem) { - this.dimension = dimension; - this.baseItem = baseItem; + public DimensionalGroupFunction(NumberItem baseNumberItem) { + this.baseNumberItem = baseNumberItem; } @Override @@ -65,26 +62,18 @@ public State[] getParameters() { return new State[0]; } - protected boolean isCompatible(@Nullable Item member) { - if (member instanceof GroupItem groupItem) { + protected boolean isCompatible(@Nullable Item memberItem) { // allow Nullable arg for recursive call below + if (memberItem instanceof GroupItem groupItem) { return isCompatible(groupItem.getBaseItem()); } - if ((baseItem instanceof NumberItem baseNI) && (member instanceof NumberItem memberNI)) { - Unit> baseUnit = baseNI.getUnit(); - Unit> memberUnit = memberNI.getUnit(); + if (memberItem instanceof NumberItem memberNumberItem) { + Unit> baseUnit = baseNumberItem.getUnit(); + Unit> memberUnit = memberNumberItem.getUnit(); if (baseUnit != null && memberUnit != null) { return baseUnit.isCompatible(memberUnit) || baseUnit.isCompatible(memberUnit.inverse()); } } - return member instanceof NumberItem memberNI && dimension.equals(memberNI.getDimension()); - } - - protected Unit getBaseUnitOrDefault(Item defaultItem) { - Unit unit = baseItem instanceof NumberItem baseNI ? baseNI.getUnit() : null; - if (unit == null) { - unit = defaultItem instanceof NumberItem defaultNI ? defaultNI.getUnit() : null; - } - return unit != null ? unit : Units.ONE; + return false; } } @@ -93,8 +82,8 @@ protected Unit getBaseUnitOrDefault(Item defaultItem) { */ class Avg extends DimensionalGroupFunction { - public Avg(Class> dimension, @Nullable Item baseItem) { - super(dimension, baseItem); + public Avg(NumberItem baseItem) { + super(baseItem); } @Override @@ -104,27 +93,27 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } - Unit unit = null; QuantityType sum = null; int count = 0; - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - if (unit == null) { - unit = getBaseUnitOrDefault(item); - } - if (sum == null) { - sum = QuantityType.valueOf(0, unit); - } - itemState = itemState.toInvertibleUnit(unit); - if (itemState != null) { - sum = sum.add(itemState); - count++; + + Unit unit = baseNumberItem.getUnit(); + if (unit != null) { + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (sum == null) { + sum = QuantityType.valueOf(0, unit); + } + itemState = itemState.toInvertibleUnit(unit); + if (itemState != null) { + sum = sum.add(itemState); + count++; + } } } @@ -142,8 +131,8 @@ public State calculate(@Nullable Set items) { */ class Median extends DimensionalGroupFunction { - public Median(Class> dimension, @Nullable Item baseItem) { - super(dimension, baseItem); + public Median(NumberItem baseItem) { + super(baseItem); } @Override @@ -152,20 +141,19 @@ public State calculate(@Nullable Set items) { if (items != null) { List values = new ArrayList<>(); - Unit unit = null; - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - if (unit == null) { - unit = getBaseUnitOrDefault(item); - } - if (itemState.toInvertibleUnit(unit) instanceof QuantityType value) { - values.add(value.toBigDecimal()); + Unit unit = baseNumberItem.getUnit(); + if (unit != null) { + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (itemState.toInvertibleUnit(unit) instanceof QuantityType value) { + values.add(value.toBigDecimal()); + } } } @@ -186,8 +174,8 @@ public State calculate(@Nullable Set items) { */ class Sum extends DimensionalGroupFunction { - public Sum(Class> dimension, @Nullable Item baseItem) { - super(dimension, baseItem); + public Sum(NumberItem baseItem) { + super(baseItem); } @Override @@ -197,25 +185,25 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } - Unit unit = null; QuantityType sum = null; - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - if (unit == null) { - unit = getBaseUnitOrDefault(item); - } - if (sum == null) { - sum = QuantityType.valueOf(0, unit); - } - itemState = itemState.toInvertibleUnit(unit); - if (itemState != null) { - sum = sum.add(itemState); + + Unit unit = baseNumberItem.getUnit(); + if (unit != null) { + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (sum == null) { + sum = QuantityType.valueOf(0, unit); + } + itemState = itemState.toInvertibleUnit(unit); + if (itemState != null) { + sum = sum.add(itemState); + } } } @@ -228,8 +216,8 @@ public State calculate(@Nullable Set items) { */ class Min extends DimensionalGroupFunction { - public Min(Class> dimension, @Nullable Item baseItem) { - super(dimension, baseItem); + public Min(NumberItem baseItem) { + super(baseItem); } @Override @@ -239,22 +227,22 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } - Unit unit = null; QuantityType min = null; - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - if (unit == null) { - unit = getBaseUnitOrDefault(item); - } - itemState = itemState.toInvertibleUnit(unit); - if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { - min = itemState; + + Unit unit = baseNumberItem.getUnit(); + if (unit != null) { + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + itemState = itemState.toInvertibleUnit(unit); + if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { + min = itemState; + } } } @@ -267,8 +255,8 @@ public State calculate(@Nullable Set items) { */ class Max extends DimensionalGroupFunction { - public Max(Class> dimension, @Nullable Item groupItem) { - super(dimension, groupItem); + public Max(NumberItem baseItem) { + super(baseItem); } @Override @@ -278,22 +266,22 @@ public State calculate(@Nullable Set items) { return UnDefType.UNDEF; } - Unit unit = null; QuantityType max = null; - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - if (unit == null) { - unit = getBaseUnitOrDefault(item); - } - itemState = itemState.toInvertibleUnit(unit); - if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { - max = itemState; + + Unit unit = baseNumberItem.getUnit(); + if (unit != null) { + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + itemState = itemState.toInvertibleUnit(unit); + if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { + max = itemState; + } } } diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java index 7eb4b7e29a9..58a895f9f55 100644 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java @@ -15,7 +15,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.number.IsCloseTo.closeTo; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.params.provider.Arguments.arguments; import java.util.LinkedHashSet; @@ -40,7 +40,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.openhab.core.i18n.UnitProvider; -import org.openhab.core.internal.i18n.TestKelvinProvider; import org.openhab.core.internal.i18n.TestUnitProvider; import org.openhab.core.items.GroupFunction; import org.openhab.core.items.GroupItem; @@ -64,6 +63,7 @@ public class QuantityTypeArithmeticGroupFunctionTest { private @Mock @NonNullByDefault({}) ComponentContext componentContext; private final UnitProvider unitProvider = new TestUnitProvider(); private final UnitProvider kelvinProvider = new TestKelvinProvider(); + private final UnitProvider mirekProvider = new TestMirekProvider(); /** * Locales having a different decimal and grouping separators to test string parsing and generation. @@ -90,7 +90,8 @@ public void testSumFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("122.41 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("234.95 °C"), state); @@ -108,7 +109,8 @@ public void testSumFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("234.95 °C"), state); @@ -124,7 +126,8 @@ public void testSumFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -142,7 +145,8 @@ public void testAvgFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("200 °C"), state); @@ -168,10 +172,12 @@ public void testAvgFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); State state = function.calculate(items); - assertEquals(new QuantityType<>("55.33333333333333333333333333333334 °C"), state); + assertTrue(state instanceof QuantityType); + assertEquals(55.33, ((QuantityType) state).doubleValue(), 0.01); } @ParameterizedTest @@ -184,12 +190,14 @@ public void testAvgFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); } + @SuppressWarnings("rawtypes") static Stream medianTestSource() { return Stream.of( // arguments( // @@ -225,7 +233,8 @@ public void testMedianFunctionQuantityType(List states, State expected) { .map(state -> createNumberItem("TestItem" + index.getAndIncrement(), Temperature.class, state)) .collect(Collectors.toSet()); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Median(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Median(baseNumberItem); State state = function.calculate(items); assertEquals(state.getClass(), expected.getClass()); @@ -247,7 +256,8 @@ public void testMaxFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("300 °C"), state); @@ -265,7 +275,8 @@ public void testMaxFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("100 °C"), state); @@ -281,7 +292,8 @@ public void testMaxFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -299,7 +311,8 @@ public void testMinFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("100 °C"), state); @@ -318,7 +331,8 @@ public void testMaxFunctionQuantityTypeOnDimensionless(Locale locale) { items.add(createNumberItem("TestItem5", Dimensionless.class, new QuantityType<>("0 %"))); items.add(createNumberItem("TestItem6", Dimensionless.class, new QuantityType<>("0 %"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Dimensionless.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Dimensionless.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("48 %"), state); @@ -336,7 +350,8 @@ public void testMinFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("294.15 K"), state); @@ -352,7 +367,8 @@ public void testMinFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -367,7 +383,8 @@ public void testSumFunctionQuantityTypeWithGroups(Locale locale) { items.add(createNumberItem("TestItem1", Power.class, new QuantityType<>("5 W"))); items.add(createGroupItem("TestGroup1", Power.class, new QuantityType<>("5 W"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Power.class, null); + NumberItem baseNumberItem = createNumberItem("BaseItem", Power.class, UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("10 W"), state); @@ -386,9 +403,21 @@ private GroupItem createGroupItem(String name, Class> dime return item; } + private NumberItem createKelvinNumberItem(String name, State state) { + NumberItem item = new NumberItem("Number:Temperature", name, kelvinProvider); + item.setState(state); + return item; + } + + private NumberItem createMirekNumberItem(String name, State state) { + NumberItem item = new NumberItem("Number:Temperature", name, mirekProvider); + item.setState(state); + return item; + } + @ParameterizedTest @MethodSource("locales") - public void testSumFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) { + public void testSumFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { Locale.setDefault(locale); Set items = new LinkedHashSet<>(); @@ -398,8 +427,8 @@ public void testSumFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K"))); - Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, baseItem); + NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("1054.40 K"), state); @@ -407,7 +436,7 @@ public void testSumFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) @ParameterizedTest @MethodSource("locales") - public void testAvgFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) { + public void testAvgFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { Locale.setDefault(locale); Set items = new LinkedHashSet<>(); @@ -417,16 +446,17 @@ public void testAvgFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, baseItem); + NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); State state = function.calculate(items); - assertEquals(new QuantityType<>("55.33333333333333333333333333333334 °C"), state); + assertTrue(state instanceof QuantityType); + assertEquals(328.48, ((QuantityType) state).doubleValue(), 0.01); } @ParameterizedTest @MethodSource("locales") - public void testMaxFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) { + public void testMaxFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { Locale.setDefault(locale); Set items = new LinkedHashSet<>(); @@ -436,8 +466,8 @@ public void testMaxFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, baseItem); + NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("100 °C"), state); @@ -445,7 +475,7 @@ public void testMaxFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) @ParameterizedTest @MethodSource("locales") - public void testMinFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) { + public void testMinFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { Locale.setDefault(locale); Set items = new LinkedHashSet<>(); @@ -455,8 +485,8 @@ public void testMinFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, baseItem); + NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("294.15 K"), state); @@ -464,7 +494,7 @@ public void testMinFunctionQuantityTypeDifferentUnitsWithBaseItem(Locale locale) @ParameterizedTest @MethodSource("locales") - public void testSumFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale locale) { + public void testSumFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale locale) { Locale.setDefault(locale); Set items = new LinkedHashSet<>(); @@ -475,8 +505,8 @@ public void testSumFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale loc items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, baseItem); + NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("8000 K"), state); @@ -484,7 +514,27 @@ public void testSumFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale loc @ParameterizedTest @MethodSource("locales") - public void testAvgFunctionQuantityTypeColorTempDifferentUnitsWithBaseItem(Locale locale) { + public void testAvgFunctionQuantityTypeColorTempDifferentUnitsBaseKelvin(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2000, Units.KELVIN))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS))); + items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED))); + items.add(createNumberItem("TestItem5", Temperature.class, + QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); + + NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("2000 K"), state); + } + + @ParameterizedTest + @MethodSource("locales") + public void testAvgFunctionQuantityTypeColorTempDifferentUnitsBaseMirek(Locale locale) { Locale.setDefault(locale); Set items = new LinkedHashSet<>(); @@ -495,8 +545,8 @@ public void testAvgFunctionQuantityTypeColorTempDifferentUnitsWithBaseItem(Local items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class, baseItem); + NumberItem baseNumberItem = createMirekNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("2000 K"), state); @@ -504,7 +554,7 @@ public void testAvgFunctionQuantityTypeColorTempDifferentUnitsWithBaseItem(Local @ParameterizedTest @MethodSource("locales") - public void testMinFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale locale) { + public void testMinFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale locale) { Locale.setDefault(locale); Set items = new LinkedHashSet<>(); @@ -515,8 +565,8 @@ public void testMinFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale loc items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class, baseItem); + NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("1999 K"), state); @@ -524,7 +574,7 @@ public void testMinFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale loc @ParameterizedTest @MethodSource("locales") - public void testMaxFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale locale) { + public void testMaxFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale locale) { Locale.setDefault(locale); Set items = new LinkedHashSet<>(); @@ -535,8 +585,8 @@ public void testMaxFunctionColorTemperatureDifferentUnitsWithBaseItem(Locale loc items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - Item baseItem = new NumberItem("Number:Temperature", "BaseItem", kelvinProvider); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class, baseItem); + NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); State state = function.calculate(items); assertEquals(new QuantityType<>("2001 K"), state); diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/internal/i18n/TestKelvinProvider.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestKelvinProvider.java similarity index 97% rename from bundles/org.openhab.core/src/test/java/org/openhab/core/internal/i18n/TestKelvinProvider.java rename to bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestKelvinProvider.java index 1760969bd64..dbf6afd50ec 100644 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/internal/i18n/TestKelvinProvider.java +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestKelvinProvider.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.internal.i18n; +package org.openhab.core.library.types; import java.util.Collection; import java.util.Set; diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestMirekProvider.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestMirekProvider.java new file mode 100644 index 00000000000..ca2645cbe80 --- /dev/null +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestMirekProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010-2025 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.library.types; + +import java.util.Collection; +import java.util.Set; + +import javax.measure.Quantity; +import javax.measure.Unit; +import javax.measure.spi.SystemOfUnits; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.i18n.UnitProvider; +import org.openhab.core.library.unit.Units; + +/** + * The {@link TestMirekProvider} implements a {@link UnitProvider} for testing purposes + * that only returns {@link Units.MIRED} + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class TestMirekProvider implements UnitProvider { + + @Override + @SuppressWarnings("unchecked") + public > Unit getUnit(Class dimension) { + return (Unit) Units.MIRED; + } + + @Override + public SystemOfUnits getMeasurementSystem() { + return Units.getInstance(); + } + + @Override + public Collection>> getAllDimensions() { + return Set.of(); + } +} From db67de9daacc16a213ff776668f78f7ae9708040 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 19 Jan 2025 20:27:33 +0000 Subject: [PATCH 05/16] refactoring Signed-off-by: Andrew Fiddian-Green --- .../internal/items/GroupFunctionHelper.java | 14 ++++++------- .../QuantityTypeArithmeticGroupFunction.java | 20 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java index 85df5ab4956..4344062fae6 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java @@ -75,21 +75,21 @@ private List parseStates(@Nullable Item baseItem, String @Nullable [] par return states; } - private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, NumberItem baseItem) { + private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, NumberItem baseNumberItem) { final String functionName = function.name; switch (functionName.toUpperCase()) { case "AVG": - return new QuantityTypeArithmeticGroupFunction.Avg(baseItem); + return new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); case "MEDIAN": - return new QuantityTypeArithmeticGroupFunction.Median(baseItem); + return new QuantityTypeArithmeticGroupFunction.Median(baseNumberItem); case "SUM": - return new QuantityTypeArithmeticGroupFunction.Sum(baseItem); + return new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); case "MIN": - return new QuantityTypeArithmeticGroupFunction.Min(baseItem); + return new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); case "MAX": - return new QuantityTypeArithmeticGroupFunction.Max(baseItem); + return new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); default: - return createDefaultGroupFunction(function, baseItem); + return createDefaultGroupFunction(function, baseNumberItem); } } diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 7134fc41c33..bb30a0f7daf 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -82,8 +82,8 @@ protected boolean isCompatible(@Nullable Item memberItem) { // allow Nullable ar */ class Avg extends DimensionalGroupFunction { - public Avg(NumberItem baseItem) { - super(baseItem); + public Avg(NumberItem baseNumberItem) { + super(baseNumberItem); } @Override @@ -131,8 +131,8 @@ public State calculate(@Nullable Set items) { */ class Median extends DimensionalGroupFunction { - public Median(NumberItem baseItem) { - super(baseItem); + public Median(NumberItem baseNumberItem) { + super(baseNumberItem); } @Override @@ -174,8 +174,8 @@ public State calculate(@Nullable Set items) { */ class Sum extends DimensionalGroupFunction { - public Sum(NumberItem baseItem) { - super(baseItem); + public Sum(NumberItem baseNumberItem) { + super(baseNumberItem); } @Override @@ -216,8 +216,8 @@ public State calculate(@Nullable Set items) { */ class Min extends DimensionalGroupFunction { - public Min(NumberItem baseItem) { - super(baseItem); + public Min(NumberItem baseNumberItem) { + super(baseNumberItem); } @Override @@ -255,8 +255,8 @@ public State calculate(@Nullable Set items) { */ class Max extends DimensionalGroupFunction { - public Max(NumberItem baseItem) { - super(baseItem); + public Max(NumberItem baseNumberItem) { + super(baseNumberItem); } @Override From 7d088b091c32e4fcb4d8a278b9283e999d355754 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 20 Jan 2025 13:21:44 +0000 Subject: [PATCH 06/16] refine targetUnit Signed-off-by: Andrew Fiddian-Green --- .../internal/items/GroupFunctionHelper.java | 31 +-- .../QuantityTypeArithmeticGroupFunction.java | 182 ++++++++---------- ...antityTypeArithmeticGroupFunctionTest.java | 84 +++----- 3 files changed, 122 insertions(+), 175 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java index 4344062fae6..aca512eab98 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java @@ -15,6 +15,8 @@ import java.util.ArrayList; import java.util.List; +import javax.measure.Unit; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.items.GroupFunction; @@ -77,20 +79,23 @@ private List parseStates(@Nullable Item baseItem, String @Nullable [] par private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, NumberItem baseNumberItem) { final String functionName = function.name; - switch (functionName.toUpperCase()) { - case "AVG": - return new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); - case "MEDIAN": - return new QuantityTypeArithmeticGroupFunction.Median(baseNumberItem); - case "SUM": - return new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); - case "MIN": - return new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); - case "MAX": - return new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); - default: - return createDefaultGroupFunction(function, baseNumberItem); + Unit targetUnit = baseNumberItem.getUnit(); + if (targetUnit != null) { + switch (functionName.toUpperCase()) { + case "AVG": + return new QuantityTypeArithmeticGroupFunction.Avg(targetUnit); + case "MEDIAN": + return new QuantityTypeArithmeticGroupFunction.Median(targetUnit); + case "SUM": + return new QuantityTypeArithmeticGroupFunction.Sum(targetUnit); + case "MIN": + return new QuantityTypeArithmeticGroupFunction.Min(targetUnit); + case "MAX": + return new QuantityTypeArithmeticGroupFunction.Max(targetUnit); + default: + } } + return createDefaultGroupFunction(function, baseNumberItem); } private GroupFunction createDefaultGroupFunction(GroupFunctionDTO function, @Nullable Item baseItem) { diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index bb30a0f7daf..b43460decca 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Set; -import javax.measure.Quantity; import javax.measure.Unit; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -41,10 +40,10 @@ public interface QuantityTypeArithmeticGroupFunction extends GroupFunction { abstract class DimensionalGroupFunction implements GroupFunction { - protected final NumberItem baseNumberItem; + protected final Unit targetUnit; - public DimensionalGroupFunction(NumberItem baseNumberItem) { - this.baseNumberItem = baseNumberItem; + public DimensionalGroupFunction(Unit targetUnit) { + this.targetUnit = targetUnit; } @Override @@ -67,11 +66,9 @@ protected boolean isCompatible(@Nullable Item memberItem) { // allow Nullable ar return isCompatible(groupItem.getBaseItem()); } if (memberItem instanceof NumberItem memberNumberItem) { - Unit> baseUnit = baseNumberItem.getUnit(); - Unit> memberUnit = memberNumberItem.getUnit(); - if (baseUnit != null && memberUnit != null) { - return baseUnit.isCompatible(memberUnit) || baseUnit.isCompatible(memberUnit.inverse()); - } + Unit memberUnit = memberNumberItem.getUnit(); + return memberUnit != null && targetUnit.isCompatible(memberUnit) + || targetUnit.isCompatible(memberUnit.inverse()); } return false; } @@ -82,8 +79,8 @@ protected boolean isCompatible(@Nullable Item memberItem) { // allow Nullable ar */ class Avg extends DimensionalGroupFunction { - public Avg(NumberItem baseNumberItem) { - super(baseNumberItem); + public Avg(Unit targetUnit) { + super(targetUnit); } @Override @@ -96,30 +93,27 @@ public State calculate(@Nullable Set items) { QuantityType sum = null; int count = 0; - Unit unit = baseNumberItem.getUnit(); - if (unit != null) { - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - if (sum == null) { - sum = QuantityType.valueOf(0, unit); - } - itemState = itemState.toInvertibleUnit(unit); - if (itemState != null) { - sum = sum.add(itemState); - count++; - } + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (sum == null) { + sum = QuantityType.valueOf(0, targetUnit); + } + itemState = itemState.toInvertibleUnit(targetUnit); + if (itemState != null) { + sum = sum.add(itemState); + count++; } } if (sum != null && count > 0) { BigDecimal result = sum.toBigDecimal().divide(BigDecimal.valueOf(count), MathContext.DECIMAL128); - return new QuantityType(result, sum.getUnit()); + return new QuantityType(result, targetUnit); } return UnDefType.UNDEF; @@ -131,8 +125,8 @@ public State calculate(@Nullable Set items) { */ class Median extends DimensionalGroupFunction { - public Median(NumberItem baseNumberItem) { - super(baseNumberItem); + public Median(Unit targetUnit) { + super(targetUnit); } @Override @@ -141,28 +135,24 @@ public State calculate(@Nullable Set items) { if (items != null) { List values = new ArrayList<>(); - Unit unit = baseNumberItem.getUnit(); - if (unit != null) { - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - if (itemState.toInvertibleUnit(unit) instanceof QuantityType value) { - values.add(value.toBigDecimal()); - } + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (itemState.toInvertibleUnit(targetUnit) instanceof QuantityType value) { + values.add(value.toBigDecimal()); } } if (!values.isEmpty()) { BigDecimal median = Statistics.median(values); - if (median != null && unit != null) { - return new QuantityType<>(median, unit); + if (median != null) { + return new QuantityType<>(median, targetUnit); } - } } return UnDefType.UNDEF; @@ -174,8 +164,8 @@ public State calculate(@Nullable Set items) { */ class Sum extends DimensionalGroupFunction { - public Sum(NumberItem baseNumberItem) { - super(baseNumberItem); + public Sum(Unit targetUnit) { + super(targetUnit); } @Override @@ -186,24 +176,20 @@ public State calculate(@Nullable Set items) { } QuantityType sum = null; - - Unit unit = baseNumberItem.getUnit(); - if (unit != null) { - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - if (sum == null) { - sum = QuantityType.valueOf(0, unit); - } - itemState = itemState.toInvertibleUnit(unit); - if (itemState != null) { - sum = sum.add(itemState); - } + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + if (sum == null) { + sum = QuantityType.valueOf(0, targetUnit); + } + itemState = itemState.toInvertibleUnit(targetUnit); + if (itemState != null) { + sum = sum.add(itemState); } } @@ -216,8 +202,8 @@ public State calculate(@Nullable Set items) { */ class Min extends DimensionalGroupFunction { - public Min(NumberItem baseNumberItem) { - super(baseNumberItem); + public Min(Unit targetUnit) { + super(targetUnit); } @Override @@ -228,21 +214,17 @@ public State calculate(@Nullable Set items) { } QuantityType min = null; - - Unit unit = baseNumberItem.getUnit(); - if (unit != null) { - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - itemState = itemState.toInvertibleUnit(unit); - if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { - min = itemState; - } + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + itemState = itemState.toInvertibleUnit(targetUnit); + if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { + min = itemState; } } @@ -255,8 +237,8 @@ public State calculate(@Nullable Set items) { */ class Max extends DimensionalGroupFunction { - public Max(NumberItem baseNumberItem) { - super(baseNumberItem); + public Max(Unit targetUnit) { + super(targetUnit); } @Override @@ -267,21 +249,17 @@ public State calculate(@Nullable Set items) { } QuantityType max = null; - - Unit unit = baseNumberItem.getUnit(); - if (unit != null) { - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = item.getStateAs(QuantityType.class); - if (itemState == null) { - continue; - } - itemState = itemState.toInvertibleUnit(unit); - if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { - max = itemState; - } + for (Item item : items) { + if (!isCompatible(item)) { + continue; + } + QuantityType itemState = item.getStateAs(QuantityType.class); + if (itemState == null) { + continue; + } + itemState = itemState.toInvertibleUnit(targetUnit); + if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { + max = itemState; } } diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java index 58a895f9f55..0d8ac37d7e1 100644 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java @@ -90,8 +90,7 @@ public void testSumFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("122.41 °C"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("234.95 °C"), state); @@ -109,8 +108,7 @@ public void testSumFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("234.95 °C"), state); @@ -126,8 +124,7 @@ public void testSumFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -145,8 +142,7 @@ public void testAvgFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("200 °C"), state); @@ -172,8 +168,7 @@ public void testAvgFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(SIUnits.CELSIUS); State state = function.calculate(items); assertTrue(state instanceof QuantityType); @@ -190,8 +185,7 @@ public void testAvgFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -233,8 +227,7 @@ public void testMedianFunctionQuantityType(List states, State expected) { .map(state -> createNumberItem("TestItem" + index.getAndIncrement(), Temperature.class, state)) .collect(Collectors.toSet()); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Median(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Median(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(state.getClass(), expected.getClass()); @@ -256,8 +249,7 @@ public void testMaxFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("300 °C"), state); @@ -275,8 +267,7 @@ public void testMaxFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("100 °C"), state); @@ -292,8 +283,7 @@ public void testMaxFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -311,8 +301,7 @@ public void testMinFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("100 °C"), state); @@ -331,8 +320,7 @@ public void testMaxFunctionQuantityTypeOnDimensionless(Locale locale) { items.add(createNumberItem("TestItem5", Dimensionless.class, new QuantityType<>("0 %"))); items.add(createNumberItem("TestItem6", Dimensionless.class, new QuantityType<>("0 %"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Dimensionless.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Units.ONE); State state = function.calculate(items); assertEquals(new QuantityType<>("48 %"), state); @@ -350,8 +338,7 @@ public void testMinFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("294.15 K"), state); @@ -367,8 +354,7 @@ public void testMinFunctionQuantityTypeIncompatibleUnits(Locale locale) { items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Temperature.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(SIUnits.CELSIUS); State state = function.calculate(items); assertEquals(new QuantityType<>("23.54 °C"), state); @@ -383,8 +369,7 @@ public void testSumFunctionQuantityTypeWithGroups(Locale locale) { items.add(createNumberItem("TestItem1", Power.class, new QuantityType<>("5 W"))); items.add(createGroupItem("TestGroup1", Power.class, new QuantityType<>("5 W"))); - NumberItem baseNumberItem = createNumberItem("BaseItem", Power.class, UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Units.WATT); State state = function.calculate(items); assertEquals(new QuantityType<>("10 W"), state); @@ -403,18 +388,6 @@ private GroupItem createGroupItem(String name, Class> dime return item; } - private NumberItem createKelvinNumberItem(String name, State state) { - NumberItem item = new NumberItem("Number:Temperature", name, kelvinProvider); - item.setState(state); - return item; - } - - private NumberItem createMirekNumberItem(String name, State state) { - NumberItem item = new NumberItem("Number:Temperature", name, mirekProvider); - item.setState(state); - return item; - } - @ParameterizedTest @MethodSource("locales") public void testSumFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { @@ -427,8 +400,7 @@ public void testSumFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K"))); - NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Units.KELVIN); State state = function.calculate(items); assertEquals(new QuantityType<>("1054.40 K"), state); @@ -446,8 +418,7 @@ public void testAvgFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.KELVIN); State state = function.calculate(items); assertTrue(state instanceof QuantityType); @@ -466,8 +437,7 @@ public void testMaxFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Units.KELVIN); State state = function.calculate(items); assertEquals(new QuantityType<>("100 °C"), state); @@ -485,8 +455,7 @@ public void testMinFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Units.KELVIN); State state = function.calculate(items); assertEquals(new QuantityType<>("294.15 K"), state); @@ -505,8 +474,7 @@ public void testSumFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale local items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Units.KELVIN); State state = function.calculate(items); assertEquals(new QuantityType<>("8000 K"), state); @@ -525,8 +493,7 @@ public void testAvgFunctionQuantityTypeColorTempDifferentUnitsBaseKelvin(Locale items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.KELVIN); State state = function.calculate(items); assertEquals(new QuantityType<>("2000 K"), state); @@ -545,8 +512,7 @@ public void testAvgFunctionQuantityTypeColorTempDifferentUnitsBaseMirek(Locale l items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - NumberItem baseNumberItem = createMirekNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.MIRED); State state = function.calculate(items); assertEquals(new QuantityType<>("2000 K"), state); @@ -565,8 +531,7 @@ public void testMinFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale local items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Units.KELVIN); State state = function.calculate(items); assertEquals(new QuantityType<>("1999 K"), state); @@ -585,8 +550,7 @@ public void testMaxFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale local items.add(createNumberItem("TestItem5", Temperature.class, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT))); - NumberItem baseNumberItem = createKelvinNumberItem("BaseItem", UnDefType.UNDEF); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(baseNumberItem); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Units.KELVIN); State state = function.calculate(items); assertEquals(new QuantityType<>("2001 K"), state); From 144030c527698e142411cae37b9820c2c1981d07 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 20 Jan 2025 16:29:43 +0000 Subject: [PATCH 07/16] remove unused classes and refine Signed-off-by: Andrew Fiddian-Green --- .../QuantityTypeArithmeticGroupFunction.java | 36 ++++++------- ...antityTypeArithmeticGroupFunctionTest.java | 2 - .../library/types/TestKelvinProvider.java | 50 ------------------- .../core/library/types/TestMirekProvider.java | 50 ------------------- 4 files changed, 15 insertions(+), 123 deletions(-) delete mode 100644 bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestKelvinProvider.java delete mode 100644 bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestMirekProvider.java diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index b43460decca..c1ec212a2e3 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -72,6 +72,10 @@ protected boolean isCompatible(@Nullable Item memberItem) { // allow Nullable ar } return false; } + + protected @Nullable QuantityType normalizedQuantityType(@Nullable State state) { + return state instanceof QuantityType quantity ? quantity.toInvertibleUnit(targetUnit) : null; + } } /** @@ -97,18 +101,15 @@ public State calculate(@Nullable Set items) { if (!isCompatible(item)) { continue; } - QuantityType itemState = item.getStateAs(QuantityType.class); + QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); if (itemState == null) { continue; } if (sum == null) { sum = QuantityType.valueOf(0, targetUnit); } - itemState = itemState.toInvertibleUnit(targetUnit); - if (itemState != null) { - sum = sum.add(itemState); - count++; - } + sum = sum.add(itemState); + count++; } if (sum != null && count > 0) { @@ -139,13 +140,11 @@ public State calculate(@Nullable Set items) { if (!isCompatible(item)) { continue; } - QuantityType itemState = item.getStateAs(QuantityType.class); + QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); if (itemState == null) { continue; } - if (itemState.toInvertibleUnit(targetUnit) instanceof QuantityType value) { - values.add(value.toBigDecimal()); - } + values.add(itemState.toBigDecimal()); } if (!values.isEmpty()) { @@ -180,17 +179,14 @@ public State calculate(@Nullable Set items) { if (!isCompatible(item)) { continue; } - QuantityType itemState = item.getStateAs(QuantityType.class); + QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); if (itemState == null) { continue; } if (sum == null) { sum = QuantityType.valueOf(0, targetUnit); } - itemState = itemState.toInvertibleUnit(targetUnit); - if (itemState != null) { - sum = sum.add(itemState); - } + sum = sum.add(itemState); } return sum != null ? sum : UnDefType.UNDEF; @@ -218,12 +214,11 @@ public State calculate(@Nullable Set items) { if (!isCompatible(item)) { continue; } - QuantityType itemState = item.getStateAs(QuantityType.class); + QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); if (itemState == null) { continue; } - itemState = itemState.toInvertibleUnit(targetUnit); - if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { + if (min == null || min.compareTo(itemState) > 0) { min = itemState; } } @@ -253,12 +248,11 @@ public State calculate(@Nullable Set items) { if (!isCompatible(item)) { continue; } - QuantityType itemState = item.getStateAs(QuantityType.class); + QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); if (itemState == null) { continue; } - itemState = itemState.toInvertibleUnit(targetUnit); - if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { + if (max == null || max.compareTo(itemState) < 0) { max = itemState; } } diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java index 0d8ac37d7e1..ce6fd853af1 100644 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java @@ -62,8 +62,6 @@ public class QuantityTypeArithmeticGroupFunctionTest { private @Mock @NonNullByDefault({}) ComponentContext componentContext; private final UnitProvider unitProvider = new TestUnitProvider(); - private final UnitProvider kelvinProvider = new TestKelvinProvider(); - private final UnitProvider mirekProvider = new TestMirekProvider(); /** * Locales having a different decimal and grouping separators to test string parsing and generation. diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestKelvinProvider.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestKelvinProvider.java deleted file mode 100644 index dbf6afd50ec..00000000000 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestKelvinProvider.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2010-2025 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.library.types; - -import java.util.Collection; -import java.util.Set; - -import javax.measure.Quantity; -import javax.measure.Unit; -import javax.measure.spi.SystemOfUnits; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.i18n.UnitProvider; -import org.openhab.core.library.unit.Units; - -/** - * The {@link TestKelvinProvider} implements a {@link UnitProvider} for testing purposes - * that only returns {@link Units.KELVIN} - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class TestKelvinProvider implements UnitProvider { - - @Override - @SuppressWarnings("unchecked") - public > Unit getUnit(Class dimension) { - return (Unit) Units.KELVIN; - } - - @Override - public SystemOfUnits getMeasurementSystem() { - return Units.getInstance(); - } - - @Override - public Collection>> getAllDimensions() { - return Set.of(); - } -} diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestMirekProvider.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestMirekProvider.java deleted file mode 100644 index ca2645cbe80..00000000000 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/TestMirekProvider.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2010-2025 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.library.types; - -import java.util.Collection; -import java.util.Set; - -import javax.measure.Quantity; -import javax.measure.Unit; -import javax.measure.spi.SystemOfUnits; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.i18n.UnitProvider; -import org.openhab.core.library.unit.Units; - -/** - * The {@link TestMirekProvider} implements a {@link UnitProvider} for testing purposes - * that only returns {@link Units.MIRED} - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class TestMirekProvider implements UnitProvider { - - @Override - @SuppressWarnings("unchecked") - public > Unit getUnit(Class dimension) { - return (Unit) Units.MIRED; - } - - @Override - public SystemOfUnits getMeasurementSystem() { - return Units.getInstance(); - } - - @Override - public Collection>> getAllDimensions() { - return Set.of(); - } -} From df8ed0246fca54ad4324215360869f0a618d7732 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 20 Jan 2025 16:34:49 +0000 Subject: [PATCH 08/16] oops eliminate potential null warning Signed-off-by: Andrew Fiddian-Green --- .../library/types/QuantityTypeArithmeticGroupFunction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index c1ec212a2e3..5917887d8f5 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -67,8 +67,8 @@ protected boolean isCompatible(@Nullable Item memberItem) { // allow Nullable ar } if (memberItem instanceof NumberItem memberNumberItem) { Unit memberUnit = memberNumberItem.getUnit(); - return memberUnit != null && targetUnit.isCompatible(memberUnit) - || targetUnit.isCompatible(memberUnit.inverse()); + return (memberUnit != null) + && (targetUnit.isCompatible(memberUnit) || targetUnit.isCompatible(memberUnit.inverse())); } return false; } From 203c4e2f56da4ba5e69a1d2e523bdb4d1bed2e96 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 20 Jan 2025 20:04:04 +0000 Subject: [PATCH 09/16] javadoc and code simplification Signed-off-by: Andrew Fiddian-Green --- .../QuantityTypeArithmeticGroupFunction.java | 101 +++++++----------- 1 file changed, 39 insertions(+), 62 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 5917887d8f5..81034005fc0 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -23,9 +23,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.items.GroupFunction; -import org.openhab.core.items.GroupItem; import org.openhab.core.items.Item; -import org.openhab.core.library.items.NumberItem; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; import org.openhab.core.util.Statistics; @@ -61,18 +59,15 @@ public State[] getParameters() { return new State[0]; } - protected boolean isCompatible(@Nullable Item memberItem) { // allow Nullable arg for recursive call below - if (memberItem instanceof GroupItem groupItem) { - return isCompatible(groupItem.getBaseItem()); - } - if (memberItem instanceof NumberItem memberNumberItem) { - Unit memberUnit = memberNumberItem.getUnit(); - return (memberUnit != null) - && (targetUnit.isCompatible(memberUnit) || targetUnit.isCompatible(memberUnit.inverse())); - } - return false; - } - + /** + * Convert the given item {@link State} to a {@link QuantityType} based on 'targetUnit'. Returns false if the + * {@link State} is not a {@link QuantityType} or if the {@link QuantityType} could not be converted to the + * 'targetUnit'. The conversion can be made to both inverted and non-inverted units, so invertible type + * conversions (e.g. Mirek <=> Kelvin) will succeed. + * + * @param state the State of the group member item + * @return a QuantityType or null + */ protected @Nullable QuantityType normalizedQuantityType(@Nullable State state) { return state instanceof QuantityType quantity ? quantity.toInvertibleUnit(targetUnit) : null; } @@ -98,18 +93,14 @@ public State calculate(@Nullable Set items) { int count = 0; for (Item item : items) { - if (!isCompatible(item)) { - continue; - } QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState == null) { - continue; - } - if (sum == null) { - sum = QuantityType.valueOf(0, targetUnit); + if (itemState != null) { + if (sum == null) { + sum = QuantityType.valueOf(0, targetUnit); + } + sum = sum.add(itemState); + count++; } - sum = sum.add(itemState); - count++; } if (sum != null && count > 0) { @@ -133,27 +124,26 @@ public Median(Unit targetUnit) { @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public State calculate(@Nullable Set items) { - if (items != null) { - List values = new ArrayList<>(); + if (items == null || items.isEmpty()) { + return UnDefType.UNDEF; + } - for (Item item : items) { - if (!isCompatible(item)) { - continue; - } - QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState == null) { - continue; - } + List values = new ArrayList<>(); + + for (Item item : items) { + QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); + if (itemState != null) { values.add(itemState.toBigDecimal()); } + } - if (!values.isEmpty()) { - BigDecimal median = Statistics.median(values); - if (median != null) { - return new QuantityType<>(median, targetUnit); - } + if (!values.isEmpty()) { + BigDecimal median = Statistics.median(values); + if (median != null) { + return new QuantityType<>(median, targetUnit); } } + return UnDefType.UNDEF; } } @@ -175,18 +165,15 @@ public State calculate(@Nullable Set items) { } QuantityType sum = null; + for (Item item : items) { - if (!isCompatible(item)) { - continue; - } QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState == null) { - continue; - } - if (sum == null) { - sum = QuantityType.valueOf(0, targetUnit); + if (itemState != null) { + if (sum == null) { + sum = QuantityType.valueOf(0, targetUnit); + } + sum = sum.add(itemState); } - sum = sum.add(itemState); } return sum != null ? sum : UnDefType.UNDEF; @@ -210,15 +197,10 @@ public State calculate(@Nullable Set items) { } QuantityType min = null; + for (Item item : items) { - if (!isCompatible(item)) { - continue; - } QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState == null) { - continue; - } - if (min == null || min.compareTo(itemState) > 0) { + if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { min = itemState; } } @@ -244,15 +226,10 @@ public State calculate(@Nullable Set items) { } QuantityType max = null; + for (Item item : items) { - if (!isCompatible(item)) { - continue; - } QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState == null) { - continue; - } - if (max == null || max.compareTo(itemState) < 0) { + if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { max = itemState; } } From efe2a86e694b7ba9c58b803510b473c0480d3861 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Tue, 21 Jan 2025 14:06:48 +0000 Subject: [PATCH 10/16] javadoc Signed-off-by: Andrew Fiddian-Green --- .../types/QuantityTypeArithmeticGroupFunction.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 81034005fc0..cdb7093f4a2 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -23,6 +23,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.items.GroupFunction; +import org.openhab.core.items.GroupItem; import org.openhab.core.items.Item; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; @@ -32,13 +33,14 @@ * This interface is a container for dimension based functions that require {@link QuantityType}s for its calculations. * * @author Henning Treu - Initial contribution + * @author Andrew Fiddian-Green - Normalise calculations based on the Unit of the GroupItem */ @NonNullByDefault public interface QuantityTypeArithmeticGroupFunction extends GroupFunction { abstract class DimensionalGroupFunction implements GroupFunction { - protected final Unit targetUnit; + protected final Unit targetUnit; // the base reference unit for all group calculations public DimensionalGroupFunction(Unit targetUnit) { this.targetUnit = targetUnit; @@ -60,12 +62,12 @@ public State[] getParameters() { } /** - * Convert the given item {@link State} to a {@link QuantityType} based on 'targetUnit'. Returns false if the - * {@link State} is not a {@link QuantityType} or if the {@link QuantityType} could not be converted to the - * 'targetUnit'. The conversion can be made to both inverted and non-inverted units, so invertible type - * conversions (e.g. Mirek <=> Kelvin) will succeed. + * Convert the given item {@link State} to a {@link QuantityType} based on the {@link Unit} of the + * {@link GroupItem} i.e. the 'targetUnit'. Returns false if the {@link State} is not a {@link QuantityType} or + * if the {@link QuantityType} could not be converted to the 'targetUnit'. The conversion can be made to both + * inverted and non-inverted units, so invertible type conversions (e.g. Mirek <=> Kelvin) will succeed. * - * @param state the State of the group member item + * @param state the State of any given group member item * @return a QuantityType or null */ protected @Nullable QuantityType normalizedQuantityType(@Nullable State state) { From 925bc0da974661407edb7d29632ba35bad9f4fbf Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 22 Jan 2025 13:38:03 +0000 Subject: [PATCH 11/16] use streams; make sums absolute Signed-off-by: Andrew Fiddian-Green --- .../internal/items/GroupFunctionHelper.java | 1 + .../QuantityTypeArithmeticGroupFunction.java | 158 +++++++----------- ...antityTypeArithmeticGroupFunctionTest.java | 25 ++- 3 files changed, 88 insertions(+), 96 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java index aca512eab98..bce7f4cee9e 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java @@ -81,6 +81,7 @@ private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, Nu final String functionName = function.name; Unit targetUnit = baseNumberItem.getUnit(); if (targetUnit != null) { + targetUnit = targetUnit.getSystemUnit(); // use system unit so 'Sum' becomes zero based (absolute) switch (functionName.toUpperCase()) { case "AVG": return new QuantityTypeArithmeticGroupFunction.Avg(targetUnit); diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index cdb7093f4a2..05c293f0bef 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -13,9 +13,9 @@ package org.openhab.core.library.types; import java.math.BigDecimal; -import java.math.MathContext; -import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.measure.Unit; @@ -40,10 +40,10 @@ public interface QuantityTypeArithmeticGroupFunction extends GroupFunction { abstract class DimensionalGroupFunction implements GroupFunction { - protected final Unit targetUnit; // the base reference unit for all group calculations + protected final Unit referenceUnit; // the reference unit for all group member calculations - public DimensionalGroupFunction(Unit targetUnit) { - this.targetUnit = targetUnit; + public DimensionalGroupFunction(Unit referenceUnit) { + this.referenceUnit = referenceUnit; } @Override @@ -63,20 +63,36 @@ public State[] getParameters() { /** * Convert the given item {@link State} to a {@link QuantityType} based on the {@link Unit} of the - * {@link GroupItem} i.e. the 'targetUnit'. Returns false if the {@link State} is not a {@link QuantityType} or - * if the {@link QuantityType} could not be converted to the 'targetUnit'. The conversion can be made to both - * inverted and non-inverted units, so invertible type conversions (e.g. Mirek <=> Kelvin) will succeed. + * {@link GroupItem} i.e. 'referenceUnit'. Returns null if the {@link State} is not a {@link QuantityType} or + * if the {@link QuantityType} could not be converted to 'referenceUnit'. + * + * The conversion can be made to both inverted and non-inverted units, so invertible type conversions (e.g. + * Mirek <=> Kelvin) are supported. * * @param state the State of any given group member item * @return a QuantityType or null */ - protected @Nullable QuantityType normalizedQuantityType(@Nullable State state) { - return state instanceof QuantityType quantity ? quantity.toInvertibleUnit(targetUnit) : null; + private @Nullable QuantityType referenceUnitQuantityType(@Nullable State state) { + return state instanceof QuantityType quantity ? quantity.toInvertibleUnit(referenceUnit) : null; + } + + /** + * Convert a set of {@link Item} to a respective list of {@link QuantityType}. Exclude any {@link Item}s whose + * current {@link State} is not a {@link QuantityType}. Convert any remaining {@link QuantityType} to the + * 'referenceUnit' and exclude any values that did not convert. + * + * @param items a list of {@link Item} + * @return a list of {@link QuantityType} converted to the 'referenceUnit' + */ + @SuppressWarnings({ "rawtypes" }) + protected List referenceUnitQuantityTypes(Set items) { + return items.stream().map(i -> i.getStateAs(QuantityType.class)).map(s -> referenceUnitQuantityType(s)) + .filter(Objects::nonNull).map(s -> (QuantityType) s).toList(); } } /** - * This calculates the numeric average over all item states of {@link QuantityType}. + * Calculates the average of a set of item states whose value could be converted to the 'referenceUnit'. */ class Avg extends DimensionalGroupFunction { @@ -87,35 +103,20 @@ public Avg(Unit targetUnit) { @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public State calculate(@Nullable Set items) { - if (items == null || items.isEmpty()) { - return UnDefType.UNDEF; - } - - QuantityType sum = null; - int count = 0; - - for (Item item : items) { - QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState != null) { - if (sum == null) { - sum = QuantityType.valueOf(0, targetUnit); - } - sum = sum.add(itemState); - count++; + if (items != null) { + List referenceUnitQuantities = referenceUnitQuantityTypes(items); + if (!referenceUnitQuantities.isEmpty()) { + return referenceUnitQuantities.stream() + .reduce(new QuantityType(0, referenceUnit), QuantityType::add) + .divide(BigDecimal.valueOf(referenceUnitQuantities.size())); } } - - if (sum != null && count > 0) { - BigDecimal result = sum.toBigDecimal().divide(BigDecimal.valueOf(count), MathContext.DECIMAL128); - return new QuantityType(result, targetUnit); - } - return UnDefType.UNDEF; } } /** - * This calculates the numeric median over all item states of {@link QuantityType}. + * Calculates the median of a set of item states whose value could be converted to the 'referenceUnit'. */ class Median extends DimensionalGroupFunction { @@ -124,34 +125,26 @@ public Median(Unit targetUnit) { } @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) public State calculate(@Nullable Set items) { - if (items == null || items.isEmpty()) { - return UnDefType.UNDEF; - } - - List values = new ArrayList<>(); - - for (Item item : items) { - QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState != null) { - values.add(itemState.toBigDecimal()); - } - } - - if (!values.isEmpty()) { - BigDecimal median = Statistics.median(values); + if (items != null) { + BigDecimal median = Statistics + .median(referenceUnitQuantityTypes(items).stream().map(q -> q.toBigDecimal()).toList()); if (median != null) { - return new QuantityType<>(median, targetUnit); + return new QuantityType<>(median, referenceUnit); } } - return UnDefType.UNDEF; } } /** - * This calculates the numeric sum over all item states of {@link QuantityType}. + * Calculates the sum of a set of item states whose value could be converted to the 'referenceUnit'. + * + * Uses the {@link QuanitityType.add()} method so the result is an incremental sum based on the 'referenceUnit'. As + * a general rule this class is instantiated with a 'referenceUnit' that is a "system unit" (which are zero based) + * so such incremental sum is in fact also an absolute sum. However the class COULD be instantiated with a "non- + * system unit" (e.g. °C, °F) in which case the result would be an incremental sum based on that unit. + * */ class Sum extends DimensionalGroupFunction { @@ -162,28 +155,19 @@ public Sum(Unit targetUnit) { @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public State calculate(@Nullable Set items) { - if (items == null || items.isEmpty()) { - return UnDefType.UNDEF; - } - - QuantityType sum = null; - - for (Item item : items) { - QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState != null) { - if (sum == null) { - sum = QuantityType.valueOf(0, targetUnit); - } - sum = sum.add(itemState); + if (items != null) { + List referenceUnitQuantities = referenceUnitQuantityTypes(items); + if (!referenceUnitQuantities.isEmpty()) { + return referenceUnitQuantities.stream().reduce(new QuantityType(0, referenceUnit), + QuantityType::add); } } - - return sum != null ? sum : UnDefType.UNDEF; + return UnDefType.UNDEF; } } /** - * This calculates the minimum value of all item states of {@link QuantityType}. + * Calculates the minimum of a set of item states whose value could be converted to the 'referenceUnit'. */ class Min extends DimensionalGroupFunction { @@ -194,25 +178,19 @@ public Min(Unit targetUnit) { @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public State calculate(@Nullable Set items) { - if (items == null || items.isEmpty()) { - return UnDefType.UNDEF; - } - - QuantityType min = null; - - for (Item item : items) { - QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState != null && (min == null || min.compareTo(itemState) > 0)) { - min = itemState; + if (items != null) { + List referenceUnitQuantities = referenceUnitQuantityTypes(items); + if (!referenceUnitQuantities.isEmpty()) { + Optional min = referenceUnitQuantities.stream().min(QuantityType::compareTo); + return min.isPresent() ? min.get() : UnDefType.UNDEF; } } - - return min != null ? min : UnDefType.UNDEF; + return UnDefType.UNDEF; } } /** - * This calculates the maximum value of all item states of {@link QuantityType}. + * Calculates the maximum of a set of item states whose value could be converted to the 'referenceUnit'. */ class Max extends DimensionalGroupFunction { @@ -223,20 +201,14 @@ public Max(Unit targetUnit) { @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public State calculate(@Nullable Set items) { - if (items == null || items.isEmpty()) { - return UnDefType.UNDEF; - } - - QuantityType max = null; - - for (Item item : items) { - QuantityType itemState = normalizedQuantityType(item.getStateAs(QuantityType.class)); - if (itemState != null && (max == null || max.compareTo(itemState) < 0)) { - max = itemState; + if (items != null) { + List referenceUnitQuantities = referenceUnitQuantityTypes(items); + if (!referenceUnitQuantities.isEmpty()) { + Optional max = referenceUnitQuantities.stream().max(QuantityType::compareTo); + return max.isPresent() ? max.get() : UnDefType.UNDEF; } } - - return max != null ? max : UnDefType.UNDEF; + return UnDefType.UNDEF; } } } diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java index ce6fd853af1..e67089eff62 100644 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunctionTest.java @@ -140,7 +140,7 @@ public void testAvgFunctionQuantityType(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(SIUnits.CELSIUS); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.KELVIN); State state = function.calculate(items); assertEquals(new QuantityType<>("200 °C"), state); @@ -166,11 +166,11 @@ public void testAvgFunctionQuantityTypeDifferentUnits(Locale locale) { items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K"))); - GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(SIUnits.CELSIUS); + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.KELVIN); State state = function.calculate(items); assertTrue(state instanceof QuantityType); - assertEquals(55.33, ((QuantityType) state).doubleValue(), 0.01); + assertEquals(328.48, ((QuantityType) state).doubleValue(), 0.01); } @ParameterizedTest @@ -219,6 +219,7 @@ static Stream medianTestSource() { @ParameterizedTest @MethodSource("medianTestSource") + @SuppressWarnings({ "null", "rawtypes", "unchecked" }) public void testMedianFunctionQuantityType(List states, State expected) { AtomicInteger index = new AtomicInteger(1); Set items = states.stream() @@ -553,4 +554,22 @@ public void testMaxFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale local assertEquals(new QuantityType<>("2001 K"), state); } + + @ParameterizedTest + @MethodSource("locales") + public void testSumFunctionQuantityTypeDifferentUnitsBaseWatt(Locale locale) { + Locale.setDefault(locale); + + Set items = new LinkedHashSet<>(); + items.add(createNumberItem("TestItem1", Power.class, new QuantityType<>("1 W"))); + items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL)); + items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("192.2 °F"))); + items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF)); + items.add(createNumberItem("TestItem5", Power.class, new QuantityType<>("3000 mW"))); + + GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Units.WATT); + State state = function.calculate(items); + + assertEquals(new QuantityType<>("4 W"), state); + } } From cc4097fee9fb46585c65455faa98bbb6531f198c Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 22 Jan 2025 15:21:46 +0000 Subject: [PATCH 12/16] code optimisation Signed-off-by: Andrew Fiddian-Green --- .../types/QuantityTypeArithmeticGroupFunction.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 05c293f0bef..8613297db62 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -179,11 +179,8 @@ public Min(Unit targetUnit) { @SuppressWarnings({ "unchecked", "rawtypes" }) public State calculate(@Nullable Set items) { if (items != null) { - List referenceUnitQuantities = referenceUnitQuantityTypes(items); - if (!referenceUnitQuantities.isEmpty()) { - Optional min = referenceUnitQuantities.stream().min(QuantityType::compareTo); - return min.isPresent() ? min.get() : UnDefType.UNDEF; - } + Optional min = referenceUnitQuantityTypes(items).stream().min(QuantityType::compareTo); + return min.isPresent() ? min.get() : UnDefType.UNDEF; } return UnDefType.UNDEF; } @@ -202,11 +199,8 @@ public Max(Unit targetUnit) { @SuppressWarnings({ "unchecked", "rawtypes" }) public State calculate(@Nullable Set items) { if (items != null) { - List referenceUnitQuantities = referenceUnitQuantityTypes(items); - if (!referenceUnitQuantities.isEmpty()) { - Optional max = referenceUnitQuantities.stream().max(QuantityType::compareTo); - return max.isPresent() ? max.get() : UnDefType.UNDEF; - } + Optional max = referenceUnitQuantityTypes(items).stream().max(QuantityType::compareTo); + return max.isPresent() ? max.get() : UnDefType.UNDEF; } return UnDefType.UNDEF; } From 05ceabec7a08f9660e115ea806c3920a4357b98c Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 22 Jan 2025 16:34:09 +0000 Subject: [PATCH 13/16] use Collectors.toList() Signed-off-by: Andrew Fiddian-Green --- .../library/types/QuantityTypeArithmeticGroupFunction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 8613297db62..2fb9efaf155 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -17,6 +17,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.measure.Unit; @@ -87,7 +88,7 @@ public State[] getParameters() { @SuppressWarnings({ "rawtypes" }) protected List referenceUnitQuantityTypes(Set items) { return items.stream().map(i -> i.getStateAs(QuantityType.class)).map(s -> referenceUnitQuantityType(s)) - .filter(Objects::nonNull).map(s -> (QuantityType) s).toList(); + .filter(Objects::nonNull).collect(Collectors.toList()); } } From 5b608507bcd5ab5efbfa1fad65ee29f951e19eea Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 22 Jan 2025 16:46:53 +0000 Subject: [PATCH 14/16] add carets Signed-off-by: Andrew Fiddian-Green --- .../library/types/QuantityTypeArithmeticGroupFunction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 2fb9efaf155..3e285a385ac 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -108,7 +108,7 @@ public State calculate(@Nullable Set items) { List referenceUnitQuantities = referenceUnitQuantityTypes(items); if (!referenceUnitQuantities.isEmpty()) { return referenceUnitQuantities.stream() - .reduce(new QuantityType(0, referenceUnit), QuantityType::add) + .reduce(new QuantityType<>(0, referenceUnit), QuantityType::add) .divide(BigDecimal.valueOf(referenceUnitQuantities.size())); } } @@ -159,7 +159,7 @@ public State calculate(@Nullable Set items) { if (items != null) { List referenceUnitQuantities = referenceUnitQuantityTypes(items); if (!referenceUnitQuantities.isEmpty()) { - return referenceUnitQuantities.stream().reduce(new QuantityType(0, referenceUnit), + return referenceUnitQuantities.stream().reduce(new QuantityType<>(0, referenceUnit), QuantityType::add); } } From 352434d4c56e0f3fd613fad84582081d4d2c2ed8 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 22 Jan 2025 19:39:19 +0000 Subject: [PATCH 15/16] eliminate extra type cast Signed-off-by: Andrew Fiddian-Green --- .../library/types/QuantityTypeArithmeticGroupFunction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 3e285a385ac..456b5c3cda9 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -87,8 +87,8 @@ public State[] getParameters() { */ @SuppressWarnings({ "rawtypes" }) protected List referenceUnitQuantityTypes(Set items) { - return items.stream().map(i -> i.getStateAs(QuantityType.class)).map(s -> referenceUnitQuantityType(s)) - .filter(Objects::nonNull).collect(Collectors.toList()); + return items.stream().map(i -> i.getState()).map(s -> referenceUnitQuantityType(s)).filter(Objects::nonNull) + .collect(Collectors.toList()); } } From 848ec59c231bb9f4c167da47d1ebc8d3e7b7d9f0 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 22 Jan 2025 22:47:51 +0000 Subject: [PATCH 16/16] revert tolist due to maven error Signed-off-by: Andrew Fiddian-Green --- .../library/types/QuantityTypeArithmeticGroupFunction.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java index 456b5c3cda9..d6855074d80 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/types/QuantityTypeArithmeticGroupFunction.java @@ -17,7 +17,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import javax.measure.Unit; @@ -88,7 +87,7 @@ public State[] getParameters() { @SuppressWarnings({ "rawtypes" }) protected List referenceUnitQuantityTypes(Set items) { return items.stream().map(i -> i.getState()).map(s -> referenceUnitQuantityType(s)).filter(Objects::nonNull) - .collect(Collectors.toList()); + .map(s -> (QuantityType) s).toList(); } }