Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Normalise GroupItem member calculations to use the GroupItem's Unit #4563

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -42,9 +43,11 @@ public interface QuantityTypeArithmeticGroupFunction extends GroupFunction {
abstract class DimensionalGroupFunction implements GroupFunction {

protected final Class<? extends Quantity<?>> dimension;
protected final @Nullable Item baseItem;

public DimensionalGroupFunction(Class<? extends Quantity<?>> dimension) {
public DimensionalGroupFunction(Class<? extends Quantity<?>> dimension, @Nullable Item baseItem) {
andrewfg marked this conversation as resolved.
Show resolved Hide resolved
this.dimension = dimension;
this.baseItem = baseItem;
}

@Override
Expand All @@ -68,15 +71,23 @@ 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;
}
}

/**
* This calculates the numeric average over all item states of {@link QuantityType}.
*/
class Avg extends DimensionalGroupFunction {

public Avg(Class<? extends Quantity<?>> dimension) {
super(dimension);
public Avg(Class<? extends Quantity<?>> dimension, @Nullable Item baseItem) {
super(dimension, baseItem);
}

@Override
Expand All @@ -86,23 +97,27 @@ public State calculate(@Nullable Set<Item> 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++;
}
}
}
andrewfg marked this conversation as resolved.
Show resolved Hide resolved
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++;
}
}

Expand All @@ -120,22 +135,17 @@ public State calculate(@Nullable Set<Item> items) {
*/
class Median extends DimensionalGroupFunction {

private @Nullable Item baseItem;

public Median(Class<? extends Quantity<?>> dimension, @Nullable Item baseItem) {
super(dimension);
this.baseItem = baseItem;
super(dimension, baseItem);
}

@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public State calculate(@Nullable Set<Item> items) {
if (items != null) {
List<BigDecimal> 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;
Expand All @@ -144,18 +154,18 @@ public State calculate(@Nullable Set<Item> 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);
}

}
Expand All @@ -169,8 +179,8 @@ public State calculate(@Nullable Set<Item> items) {
*/
class Sum extends DimensionalGroupFunction {

public Sum(Class<? extends Quantity<?>> dimension) {
super(dimension);
public Sum(Class<? extends Quantity<?>> dimension, @Nullable Item baseItem) {
super(dimension, baseItem);
}

@Override
Expand All @@ -180,20 +190,25 @@ public State calculate(@Nullable Set<Item> 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);
}
}

Expand All @@ -206,8 +221,8 @@ public State calculate(@Nullable Set<Item> items) {
*/
class Min extends DimensionalGroupFunction {

public Min(Class<? extends Quantity<?>> dimension) {
super(dimension);
public Min(Class<? extends Quantity<?>> dimension, @Nullable Item baseItem) {
super(dimension, baseItem);
}

@Override
Expand All @@ -217,16 +232,22 @@ public State calculate(@Nullable Set<Item> 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;
}
}

Expand All @@ -239,8 +260,8 @@ public State calculate(@Nullable Set<Item> items) {
*/
class Max extends DimensionalGroupFunction {

public Max(Class<? extends Quantity<?>> dimension) {
super(dimension);
public Max(Class<? extends Quantity<?>> dimension, @Nullable Item baseItem) {
super(dimension, baseItem);
}

@Override
Expand All @@ -250,16 +271,22 @@ public State calculate(@Nullable Set<Item> 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;
}
}

Expand Down
Loading