diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java index e132e2a8382..8c1031f5a2b 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java @@ -71,6 +71,7 @@ * @author Mark Herwege - add median methods * @author Mark Herwege - use item lastChange and lastUpdate methods if not in peristence * @author Mark Herwege - add Riemann sum methods + * @author Mark Herwege - use base unit for calculations and results */ @Component(immediate = true) @NonNullByDefault @@ -1158,6 +1159,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param timestamp the point in time from which to compute the variance * @return the variance between then and now, or null if timestamp is in the future, if @@ -1172,6 +1177,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the variance of the state of the given {@link Item} since a certain point in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param timestamp the point in time from which to compute the variance * @param type LEFT, RIGHT, MIDPOINT or TRAPEZOIDAL representing approximation types for Riemann sums @@ -1188,6 +1197,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param timestamp the point in time to which to compute the variance * @return the variance between now and then, or null if timestamp is in the past, if @@ -1202,6 +1215,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the variance of the state of the given {@link Item} until a certain point in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param timestamp the point in time to which to compute the variance * @param type LEFT, RIGHT, MIDPOINT or TRAPEZOIDAL representing approximation types for Riemann sums @@ -1218,6 +1235,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param begin the point in time from which to compute * @param end the end time for the computation @@ -1237,6 +1258,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the variance of the state of the given {@link Item} between two certain point in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param begin the point in time from which to compute the variance * @param end the end time for the computation @@ -1256,6 +1281,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param timestamp the point in time from which to compute the variance * @param serviceId the name of the {@link PersistenceService} to use @@ -1272,6 +1301,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the variance of the state of the given {@link Item} since a certain point in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param timestamp the point in time from which to compute the variance * @param type LEFT, RIGHT, MIDPOINT or TRAPEZOIDAL representing approximation types for Riemann sums @@ -1291,6 +1324,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param timestamp the point in time to which to compute the variance * @param serviceId the name of the {@link PersistenceService} to use @@ -1307,6 +1344,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the variance of the state of the given {@link Item} until a certain point in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param timestamp the point in time to which to compute the variance * @param type LEFT, RIGHT, MIDPOINT or TRAPEZOIDAL representing approximation types for Riemann sums @@ -1326,6 +1367,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param begin the point in time from which to compute the variance * @param end the end time for the computation @@ -1342,6 +1387,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the variance of the state of the given {@link Item} between two points in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the variance for * @param begin the point in time from which to compute * @param end the end time for the computation @@ -1381,8 +1430,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable it = result.iterator(); } Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; - Unit unit = (baseItem instanceof NumberItem numberItem) - && (numberItem.getUnit() instanceof Unit numberItemUnit) ? numberItemUnit.getSystemUnit() : null; + Unit unit = (baseItem instanceof NumberItem numberItem) ? numberItem.getUnit() : null; BigDecimal average = average(beginTime, endTime, it, unit, type); if (average != null) { @@ -1417,6 +1465,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1435,6 +1487,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the standard deviation of the state of the given {@link Item} since a certain point in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1455,6 +1511,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1473,6 +1533,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the standard deviation of the state of the given {@link Item} until a certain point in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1493,6 +1557,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1512,6 +1580,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the standard deviation of the state of the given {@link Item} between two points in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1534,6 +1606,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1553,6 +1629,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the standard deviation of the state of the given {@link Item} since a certain point in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1575,6 +1655,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1594,6 +1678,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the standard deviation of the state of the given {@link Item} until a certain point in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1616,6 +1704,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * A left approximation type is used for the Riemann sum. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1638,6 +1730,10 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the standard deviation of the state of the given {@link Item} between two points in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * Note: If you need variance and standard deviation at the same time do not query both as it is a costly * operation. Get the variance only, it is the squared deviation. * @@ -1664,21 +1760,23 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable return null; } State variance = internalVarianceBetween(item, begin, end, type, effectiveServiceId); + if (variance == null) { + return null; + } - if (variance != null) { - DecimalType dt = variance.as(DecimalType.class); - // avoid ArithmeticException if variance is less than zero - if (dt != null && DecimalType.ZERO.compareTo(dt) <= 0) { - BigDecimal deviation = dt.toBigDecimal().sqrt(MathContext.DECIMAL64); - Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; - Unit unit = (baseItem instanceof NumberItem numberItem) - && (numberItem.getUnit() instanceof Unit numberItemUnit) ? numberItemUnit.getSystemUnit() - : null; - if (unit != null) { - return new QuantityType<>(deviation, unit); - } else { - return new DecimalType(deviation); - } + Unit varianceUnit = (variance instanceof QuantityType quantity) ? quantity.getUnit() : null; + DecimalType dt = variance.as(DecimalType.class); + + // avoid ArithmeticException if variance is less than zero + if (dt != null && DecimalType.ZERO.compareTo(dt) <= 0) { + BigDecimal deviation = dt.toBigDecimal().sqrt(MathContext.DECIMAL64); + + Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; + Unit unit = baseItem instanceof NumberItem numberItem ? numberItem.getUnit() : null; + if (varianceUnit != null && unit != null) { + return (new QuantityType<>(deviation, varianceUnit.root(2))).toUnit(unit); + } else { + return new DecimalType(deviation); } } return null; @@ -1938,10 +2036,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the Riemann sum of the states of a given {@link Item} since a certain point in time. * This can be used as an approximation for integrating the curve represented by discrete values. * A left approximation type is used for the Riemann sum. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param timestamp the point in time from which to search for the riemannSum value * @return the Riemann sum since timestamp or null if no @@ -1955,10 +2059,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable /** * Gets the Riemann sum of the states of a given {@link Item} since a certain point in time. * This can be used as an approximation for integrating the curve represented by discrete values. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param timestamp the point in time from which to search for the riemannSum value * @param type LEFT, RIGHT, MIDPOINT or TRAPEZOIDAL representing approximation types for Riemann sums @@ -1974,10 +2084,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the Riemann sum of the states of a given {@link Item} until a certain point in time. * This can be used as an approximation for integrating the curve represented by discrete values. * A left approximation type is used for the Riemann sum. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param timestamp the point in time to which to search for the riemannSum value * @return the Riemann sum until timestamp or null if no @@ -1991,10 +2107,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable /** * Gets the Riemann sum of the states of a given {@link Item} until a certain point in time. * This can be used as an approximation for integrating the curve represented by discrete values. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param timestamp the point in time to which to search for the riemannSum value * @param type LEFT, RIGHT, MIDPOINT or TRAPEZOIDAL representing approximation types for Riemann sums @@ -2010,10 +2132,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the Riemann sum of the states of a given {@link Item} between two certain points in time. * This can be used as an approximation for integrating the curve represented by discrete values. * A left approximation type is used for the Riemann sum. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param begin the point in time from which to start the summation * @param end the point in time to which to start the summation @@ -2028,10 +2156,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable /** * Gets the Riemann sum of the states of a given {@link Item} between two certain points in time. * This can be used as an approximation for integrating the curve represented by discrete values. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param begin the point in time from which to start the summation * @param end the point in time to which to start the summation @@ -2049,10 +2183,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the Riemann sum of the states of a given {@link Item} since a certain point in time. * This can be used as an approximation for integrating the curve represented by discrete values. * A left approximation type is used for the Riemann sum. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param timestamp the point in time from which to search for the riemannSum value * @param serviceId the name of the {@link PersistenceService} to use @@ -2068,10 +2208,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable /** * Gets the Riemann sum of the states of a given {@link Item} since a certain point in time. * This can be used as an approximation for integrating the curve represented by discrete values. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param timestamp the point in time from which to search for the riemannSum value * @param type LEFT, RIGHT, MIDPOINT or TRAPEZOIDAL representing approximation types for Riemann sums @@ -2090,10 +2236,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the Riemann sum of the states of a given {@link Item} until a certain point in time. * This can be used as an approximation for integrating the curve represented by discrete values. * A left approximation type is used for the Riemann sum. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param timestamp the point in time to which to search for the riemannSum value * @param serviceId the name of the {@link PersistenceService} to use @@ -2109,10 +2261,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable /** * Gets the Riemann sum of the states of a given {@link Item} until a certain point in time. * This can be used as an approximation for integrating the curve represented by discrete values. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param timestamp the point in time to which to search for the riemannSum value * @param type LEFT, RIGHT, MIDPOINT or TRAPEZOIDAL representing approximation types for Riemann sums @@ -2131,10 +2289,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable * Gets the Riemann sum of the states of a given {@link Item} between two certain points in time. * This can be used as an approximation for integrating the curve represented by discrete values. * A left approximation type is used for the Riemann sum. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param begin the point in time from which to start the summation * @param end the point in time to which to start the summation @@ -2151,10 +2315,16 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable /** * Gets the Riemann sum of the states of a given {@link Item} between two certain points in time. * This can be used as an approximation for integrating the curve represented by discrete values. - * The time dimension in the result is in seconds, therefore if you do not use QuantityType results, you may have to + * + * Note: The time dimension in the result is in seconds, therefore if you do not use QuantityType results, + * you may have to * multiply or divide to get the result in the expected scale. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the riemannSum value for * @param begin the point in time from which to start the summation * @param end the point in time to which to start the summation @@ -2193,8 +2363,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable } Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; - Unit unit = (baseItem instanceof NumberItem numberItem) - && (numberItem.getUnit() instanceof Unit numberItemUnit) ? numberItemUnit.getSystemUnit() : null; + Unit unit = (baseItem instanceof NumberItem numberItem) ? numberItem.getUnit() : null; BigDecimal sum = riemannSum(beginTime, endTime, it, unit, type).scaleByPowerOfTen(-3); if (unit != null) { return new QuantityType<>(sum, unit.multiply(Units.SECOND)); @@ -2443,6 +2612,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * value. * The default persistence service is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item for which we will sum its persisted state values since timestamp * @param timestamp the point in time from which to start the summation * @return the sum of the state values since timestamp, or null if timestamp is in the @@ -2458,6 +2631,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * value. * The default persistence service is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item for which we will sum its persisted state values to timestamp * @param timestamp the point in time to which to start the summation * @return the sum of the state values until timestamp, or null if timestamp is in the @@ -2473,6 +2650,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * value. * The default persistence service is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item for which we will sum its persisted state values between begin and * end * @param begin the point in time from which to start the summation @@ -2491,6 +2672,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * value. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item for which we will sum its persisted state values since timestamp * @param timestamp the point in time from which to start the summation * @param serviceId the name of the {@link PersistenceService} to use @@ -2507,6 +2692,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * value. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item for which we will sum its persisted state values to timestamp * @param timestamp the point in time to which to start the summation * @param serviceId the name of the {@link PersistenceService} to use @@ -2523,6 +2712,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * value. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item for which we will sum its persisted state values between begin and * end * @param begin the point in time from which to start the summation @@ -2547,8 +2740,7 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite Iterator it = result.iterator(); Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; - Unit unit = (baseItem instanceof NumberItem numberItem) - && (numberItem.getUnit() instanceof Unit numberItemUnit) ? numberItemUnit.getSystemUnit() : null; + Unit unit = baseItem instanceof NumberItem numberItem ? numberItem.getUnit() : null; BigDecimal sum = BigDecimal.ZERO; while (it.hasNext()) { HistoricItem historicItem = it.next(); @@ -2681,10 +2873,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite } if (begin == null && end != null && end.isAfter(ZonedDateTime.now())) { - valueStart = getItemValue(item); + valueStart = getItemValue(item, unit); } if (begin != null && end == null && begin.isBefore(ZonedDateTime.now())) { - valueStop = getItemValue(item); + valueStop = getItemValue(item, unit); } if (valueStart != null && valueStop != null) { @@ -2700,6 +2892,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * * This method has been deprecated and {@link #evolutionRateSince(Item, ZonedDateTime)} should be used instead. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item to get the evolution rate value for * @param timestamp the point in time from which to compute the evolution rate * @return the evolution rate in percent (positive and negative) between now and then, or null if @@ -2719,6 +2915,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * Gets the evolution rate of the state of a given {@link Item} since a certain point in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item to get the evolution rate value for * @param timestamp the point in time from which to compute the evolution rate * @return the evolution rate in percent (positive and negative) between now and then, or null if @@ -2735,6 +2935,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * Gets the evolution rate of the state of a given {@link Item} until a certain point in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item to get the evolution rate value for * @param timestamp the point in time to which to compute the evolution rate * @return the evolution rate in percent (positive and negative) between then and now, or null if @@ -2754,6 +2958,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * This method has been deprecated and {@link #evolutionRateBetween(Item, ZonedDateTime, ZonedDateTime)} should be * used instead. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item to get the evolution rate value for * @param begin the beginning point in time * @param end the end point in time @@ -2774,6 +2982,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * Gets the evolution rate of the state of a given {@link Item} between two points in time. * The default {@link PersistenceService} is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the item to get the evolution rate value for * @param begin the beginning point in time * @param end the end point in time @@ -2794,6 +3006,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * This method has been deprecated and {@link #evolutionRateSince(Item, ZonedDateTime, String)} should be used * instead. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the evolution rate value for * @param timestamp the point in time from which to compute the evolution rate * @param serviceId the name of the {@link PersistenceService} to use @@ -2815,6 +3031,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * Gets the evolution rate of the state of a given {@link Item} since a certain point in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the evolution rate value for * @param timestamp the point in time from which to compute the evolution rate * @param serviceId the name of the {@link PersistenceService} to use @@ -2834,6 +3054,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * Gets the evolution rate of the state of a given {@link Item} until a certain point in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the evolution rate value for * @param timestamp the point in time to which to compute the evolution rate * @param serviceId the name of the {@link PersistenceService} to use @@ -2856,6 +3080,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * This method has been deprecated and {@link #evolutionRateBetween(Item, ZonedDateTime, ZonedDateTime, String)} * should be used instead. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the evolution rate value for * @param begin the beginning point in time * @param end the end point in time @@ -2879,6 +3107,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite * Gets the evolution rate of the state of a given {@link Item} between two points in time. * The {@link PersistenceService} identified by the serviceId is used. * + * Note: If the {@link Item} has a dimension, the calculation will be done using the {@link Item}'s + * configured unit. + * For temperatures, this will give different results for different configured units. + * * @param item the {@link Item} to get the evolution rate value for * @param begin the beginning point in time * @param end the end point in time @@ -2918,10 +3150,10 @@ private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Ite } if (begin == null && end != null && end.isAfter(ZonedDateTime.now())) { - valueStart = getItemValue(item); + valueStart = getItemValue(item, unit); } if (begin != null && end == null && begin.isBefore(ZonedDateTime.now())) { - valueStop = getItemValue(item); + valueStop = getItemValue(item, unit); } if (valueStart != null && valueStop != null && !valueStart.equals(DecimalType.ZERO)) { @@ -3487,16 +3719,12 @@ private static void internalRemoveAllStatesBetween(Item item, @Nullable ZonedDat return null; } - private static @Nullable DecimalType getItemValue(Item item) { - Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; - if (baseItem instanceof NumberItem numberItem) { - Unit unit = numberItem.getUnit(); - if (unit != null) { - QuantityType qt = item.getStateAs(QuantityType.class); - qt = (qt != null) ? qt.toUnit(unit) : qt; - if (qt != null) { - return new DecimalType(qt.toBigDecimal()); - } + private static @Nullable DecimalType getItemValue(Item item, @Nullable Unit unit) { + if (unit != null) { + QuantityType qt = item.getStateAs(QuantityType.class); + qt = (qt != null) ? qt.toUnit(unit) : qt; + if (qt != null) { + return new DecimalType(qt.toBigDecimal()); } } return item.getStateAs(DecimalType.class); diff --git a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java index b6c7ab00e5e..d2dfe189443 100644 --- a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java +++ b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java @@ -45,9 +45,11 @@ import org.openhab.core.items.ItemRegistry; import org.openhab.core.items.ItemUtil; import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.dimension.EnergyPrice; import org.openhab.core.library.types.ArithmeticGroupFunction; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.CurrencyUnits; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.persistence.HistoricItem; @@ -70,6 +72,7 @@ * @author Mark Herwege - Implement aliases * @author Mark Herwege - Add Riemann sum methods * @author Mark Herwege - Make tests less impacted by the current time for slow builds, improves test reliability + * @author Mark Herwege - use base unit for calculations and results */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -80,8 +83,7 @@ public class PersistenceExtensionsTest { public static final String TEST_QUANTITY_NUMBER = "testQuantityNumber"; public static final String TEST_GROUP_QUANTITY_NUMBER = "testGroupQuantityNumber"; public static final String TEST_SWITCH = "testSwitch"; - - public static final double KELVIN_OFFSET = 273.15; + public static final String TEST_ENERGYPRICE_QUANTITY_NUMBER = "testEnergyPriceQuantityItem"; private @Mock @NonNullByDefault({}) ItemRegistry itemRegistryMock; private @Mock @NonNullByDefault({}) UnitProvider unitProviderMock; @@ -89,17 +91,22 @@ public class PersistenceExtensionsTest { private @Mock @NonNullByDefault({}) PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistryMock; - private @NonNullByDefault({}) GenericItem numberItem, quantityItem, groupQuantityItem, switchItem; + private @NonNullByDefault({}) GenericItem numberItem, quantityItem, groupQuantityItem, switchItem, + energyPriceQuantityItem; @BeforeEach public void setUp() { when(unitProviderMock.getUnit(Temperature.class)).thenReturn(SIUnits.CELSIUS); + when(unitProviderMock.getUnit(EnergyPrice.class)).thenReturn(CurrencyUnits.BASE_ENERGY_PRICE); CoreItemFactory itemFactory = new CoreItemFactory(unitProviderMock); numberItem = itemFactory.createItem(CoreItemFactory.NUMBER, TEST_NUMBER); quantityItem = itemFactory.createItem(CoreItemFactory.NUMBER + ItemUtil.EXTENSION_SEPARATOR + "Temperature", TEST_QUANTITY_NUMBER); switchItem = itemFactory.createItem(CoreItemFactory.SWITCH, TEST_SWITCH); + energyPriceQuantityItem = itemFactory.createItem( + CoreItemFactory.NUMBER + ItemUtil.EXTENSION_SEPARATOR + "EnergyPrice", + TEST_ENERGYPRICE_QUANTITY_NUMBER); GenericItem baseItem = itemFactory .createItem(CoreItemFactory.NUMBER + ItemUtil.EXTENSION_SEPARATOR + "Temperature", "testGroupBaseItem"); @@ -111,11 +118,13 @@ public void setUp() { quantityItem.setState(new QuantityType(STATE, SIUnits.CELSIUS)); groupQuantityItem.setState(new QuantityType(STATE, SIUnits.CELSIUS)); switchItem.setState(SWITCH_STATE); + energyPriceQuantityItem.setState(new QuantityType(STATE, CurrencyUnits.BASE_ENERGY_PRICE)); when(itemRegistryMock.get(TEST_NUMBER)).thenReturn(numberItem); when(itemRegistryMock.get(TEST_QUANTITY_NUMBER)).thenReturn(quantityItem); when(itemRegistryMock.get(TEST_SWITCH)).thenReturn(switchItem); when(itemRegistryMock.get(TEST_GROUP_QUANTITY_NUMBER)).thenReturn(groupQuantityItem); + when(itemRegistryMock.get(TEST_ENERGYPRICE_QUANTITY_NUMBER)).thenReturn(energyPriceQuantityItem); when(persistenceServiceConfigurationRegistryMock.get(anyString())).thenReturn(null); when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.systemDefault()); @@ -341,6 +350,15 @@ public void testPersistedStateOnOffType() { assertEquals(switchValue(SWITCH_ON_INTERMEDIATE_3), historicItem.getState()); } + @Test + public void testPersistedStateEnergyPriceQuantityType() { + HistoricItem historicItem = PersistenceExtensions.persistedState(energyPriceQuantityItem, + ZonedDateTime.of(HISTORIC_END, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); + assertNotNull(historicItem); + assertThat(historicItem.getState(), is(instanceOf(QuantityType.class))); + assertEquals(new QuantityType<>(value(HISTORIC_END), CurrencyUnits.BASE_ENERGY_PRICE), historicItem.getState()); + } + @Test public void testMaximumSinceDecimalType() { HistoricItem historicItem = PersistenceExtensions.maximumSince(numberItem, @@ -628,6 +646,17 @@ public void testMaximumUntilOnOffType() { assertNull(historicItem); } + @Test + public void testMaximumBetweenEnergyPriceQuantityType() { + HistoricItem historicItem = PersistenceExtensions.maximumBetween(energyPriceQuantityItem, + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); + assertNotNull(historicItem); + assertThat(historicItem.getState(), is(instanceOf(QuantityType.class))); + assertThat(historicItem.getState(), + is(new QuantityType<>(value(HISTORIC_INTERMEDIATE_VALUE_2), CurrencyUnits.BASE_ENERGY_PRICE))); + } + @Test public void testMinimumSinceDecimalType() { HistoricItem historicItem = PersistenceExtensions.minimumSince(numberItem, @@ -889,6 +918,17 @@ public void testMinimumSinceOnOffType() { assertNull(historicItem); } + @Test + public void testMinimumBetweenEnergyPriceQuantityType() { + HistoricItem historicItem = PersistenceExtensions.minimumBetween(energyPriceQuantityItem, + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); + assertNotNull(historicItem); + assertThat(historicItem.getState(), is(instanceOf(QuantityType.class))); + assertThat(historicItem.getState(), + is(new QuantityType<>(value(HISTORIC_INTERMEDIATE_VALUE_1), CurrencyUnits.BASE_ENERGY_PRICE))); + } + @Test public void testVarianceSinceDecimalType() { ZonedDateTime startStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, @@ -994,7 +1034,7 @@ public void testVarianceSinceQuantityType() { QuantityType qt = variance.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); // default persistence service variance = PersistenceExtensions.varianceSince(quantityItem, startStored); @@ -1017,7 +1057,7 @@ public void testVarianceUntilQuantityType() { QuantityType qt = variance.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); // default persistence service variance = PersistenceExtensions.varianceUntil(quantityItem, endStored); @@ -1039,7 +1079,7 @@ public void testVarianceBetweenQuantityType() { QuantityType qt = variance.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); startStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_4, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -1052,7 +1092,7 @@ public void testVarianceBetweenQuantityType() { qt = variance.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); startStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -1067,7 +1107,7 @@ public void testVarianceBetweenQuantityType() { qt = variance.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); // default persistence service variance = PersistenceExtensions.varianceBetween(quantityItem, startStored, endStored); @@ -1089,7 +1129,7 @@ public void testVarianceSinceGroupQuantityType() { QuantityType qt = variance.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); // default persistence service variance = PersistenceExtensions.varianceSince(groupQuantityItem, startStored); @@ -1112,7 +1152,7 @@ public void testVarianceUntilGroupQuantityType() { QuantityType qt = variance.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); // default persistence service variance = PersistenceExtensions.varianceUntil(groupQuantityItem, endStored); @@ -1134,7 +1174,7 @@ public void testVarianceBetweenGroupQuantityType() { QuantityType qt = variance.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); startStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_4, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -1147,7 +1187,7 @@ public void testVarianceBetweenGroupQuantityType() { qt = variance.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); startStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -1162,13 +1202,34 @@ public void testVarianceBetweenGroupQuantityType() { qt = variance.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.KELVIN), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(SIUnits.CELSIUS), qt.getUnit()); // default persistence service variance = PersistenceExtensions.varianceBetween(groupQuantityItem, startStored, endStored); assertNull(variance); } + @Test + public void testVarianceBetweenEnergyPriceQuantityType() { + ZonedDateTime startStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + ZonedDateTime endStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + double expectedAverage1 = testAverage(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2); + + double expected = IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2) + .mapToDouble(i -> Double.valueOf(i)).map(d -> Math.pow(d - expectedAverage1, 2)).sum() + / (HISTORIC_INTERMEDIATE_VALUE_2 - HISTORIC_INTERMEDIATE_VALUE_1 + 1); + + State variance = PersistenceExtensions.varianceBetween(energyPriceQuantityItem, startStored, endStored, + SERVICE_ID); + assertNotNull(variance); + QuantityType qt = variance.as(QuantityType.class); + assertNotNull(qt); + assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); + assertEquals(CurrencyUnits.BASE_ENERGY_PRICE.multiply(CurrencyUnits.BASE_ENERGY_PRICE), qt.getUnit()); + } + @Test public void testDeviationSinceDecimalType() { ZonedDateTime startStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, @@ -1274,7 +1335,7 @@ public void testDeviationSinceQuantityType() { QuantityType qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service deviation = PersistenceExtensions.deviationSince(quantityItem, startStored); @@ -1297,7 +1358,7 @@ public void testDeviationUntilQuantityType() { QuantityType qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service deviation = PersistenceExtensions.deviationUntil(quantityItem, endStored); @@ -1319,7 +1380,7 @@ public void testDeviationBetweenQuantityType() { QuantityType qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); startStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_4, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -1332,7 +1393,7 @@ public void testDeviationBetweenQuantityType() { qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); startStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -1347,7 +1408,7 @@ public void testDeviationBetweenQuantityType() { qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service deviation = PersistenceExtensions.deviationBetween(quantityItem, startStored, endStored); @@ -1369,7 +1430,7 @@ public void testDeviationSinceGroupQuantityType() { QuantityType qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service deviation = PersistenceExtensions.deviationSince(groupQuantityItem, startStored); @@ -1392,7 +1453,7 @@ public void testDeviationUntilGroupQuantityType() { QuantityType qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service deviation = PersistenceExtensions.deviationUntil(groupQuantityItem, endStored); @@ -1414,7 +1475,7 @@ public void testDeviationBetweenGroupQuantityType() { QuantityType qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); startStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_4, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -1427,7 +1488,7 @@ public void testDeviationBetweenGroupQuantityType() { qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); startStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -1442,13 +1503,33 @@ public void testDeviationBetweenGroupQuantityType() { qt = deviation.as(QuantityType.class); assertNotNull(qt); assertEquals(expected, qt.doubleValue(), 0.01); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service deviation = PersistenceExtensions.deviationBetween(groupQuantityItem, startStored, endStored); assertNull(deviation); } + @Test + public void testDeviationBetweenEnergyPriceQuantityType() { + ZonedDateTime startStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + ZonedDateTime endStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + double expectedAverage = testAverage(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2); + + double expected = Math.sqrt(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2) + .mapToDouble(i -> Double.parseDouble(Integer.toString(i))).map(d -> Math.pow(d - expectedAverage, 2)) + .sum() / (HISTORIC_INTERMEDIATE_VALUE_2 - HISTORIC_INTERMEDIATE_VALUE_1 + 1)); + State deviation = PersistenceExtensions.deviationBetween(energyPriceQuantityItem, startStored, endStored, + SERVICE_ID); + assertNotNull(deviation); + QuantityType qt = deviation.as(QuantityType.class); + assertNotNull(qt); + assertEquals(expected, qt.doubleValue(), 0.01); + assertEquals(CurrencyUnits.BASE_ENERGY_PRICE, qt.getUnit()); + } + @Test public void testRiemannSumBetweenDecimalType() { RiemannType type = RiemannType.LEFT; @@ -1593,33 +1674,33 @@ public void testRiemannSumBetweenQuantityType() { ZoneId.systemDefault()); ZonedDateTime endStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); - double expected = testRiemannSumCelsius(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2, type); + double expected = testRiemannSum(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2, type); State sum = PersistenceExtensions.riemannSumBetween(quantityItem, beginStored, endStored, type, SERVICE_ID); assertNotNull(sum); QuantityType qt = sum.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.SECOND), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(Units.SECOND), qt.getUnit()); beginStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_4, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); - expected = testRiemannSumCelsius(FUTURE_INTERMEDIATE_VALUE_3, FUTURE_INTERMEDIATE_VALUE_4, type); + expected = testRiemannSum(FUTURE_INTERMEDIATE_VALUE_3, FUTURE_INTERMEDIATE_VALUE_4, type); sum = PersistenceExtensions.riemannSumBetween(quantityItem, beginStored, endStored, type, SERVICE_ID); assertNotNull(sum); qt = sum.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.SECOND), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(Units.SECOND), qt.getUnit()); beginStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); endStored = ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); - expected = testRiemannSumCelsius(HISTORIC_INTERMEDIATE_VALUE_1, FUTURE_INTERMEDIATE_VALUE_3, type); + expected = testRiemannSum(HISTORIC_INTERMEDIATE_VALUE_1, FUTURE_INTERMEDIATE_VALUE_3, type); sum = PersistenceExtensions.riemannSumBetween(quantityItem, beginStored, endStored, type, SERVICE_ID); assertNotNull(sum); qt = sum.as(QuantityType.class); assertNotNull(qt); assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); - assertEquals(Units.KELVIN.multiply(Units.SECOND), qt.getUnit()); + assertEquals(SIUnits.CELSIUS.multiply(Units.SECOND), qt.getUnit()); // default persistence service sum = PersistenceExtensions.riemannSumBetween(quantityItem, beginStored, endStored, type); @@ -1718,6 +1799,25 @@ public void testRiemannSumBetweenZeroDuration() { assertThat(dt.doubleValue(), is(closeTo(0, 0.01))); } + @Test + public void testRiemannSumBetweenEnergyPriceQuantityType() { + for (RiemannType type : RiemannType.values()) { + ZonedDateTime beginStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + ZonedDateTime endStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + double expected = testRiemannSum(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2, type); + State sum = PersistenceExtensions.riemannSumBetween(energyPriceQuantityItem, beginStored, endStored, type, + SERVICE_ID); + + assertNotNull(sum); + QuantityType qt = sum.as(QuantityType.class); + assertNotNull(qt); + assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); + assertEquals(CurrencyUnits.BASE_ENERGY_PRICE.multiply(Units.SECOND), qt.getUnit()); + } + } + @Test public void testAverageSinceDecimalType() { ZonedDateTime start = ZonedDateTime.of(BEFORE_START, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -2086,6 +2186,23 @@ public void testAverageBetweenZeroDuration() { assertEquals(SIUnits.CELSIUS, qt.getUnit()); } + @Test + public void testAverageBetweenEnergyPriceQuantityType() { + ZonedDateTime beginStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + ZonedDateTime endStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + double expected = testAverage(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2); + State average = PersistenceExtensions.averageBetween(energyPriceQuantityItem, beginStored, endStored, + SERVICE_ID); + + assertNotNull(average); + QuantityType qt = average.as(QuantityType.class); + assertNotNull(qt); + assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); + assertEquals(CurrencyUnits.BASE_ENERGY_PRICE, qt.getUnit()); + } + @Test public void testMedianSinceDecimalType() { ZonedDateTime start = ZonedDateTime.of(BEFORE_START, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); @@ -2331,6 +2448,22 @@ public void testMedianBetweenZeroDuration() { assertEquals(SIUnits.CELSIUS, qt.getUnit()); } + @Test + public void testMedianBetweenEnergyPriceQuantityType() { + ZonedDateTime beginStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + ZonedDateTime endStored = ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, + ZoneId.systemDefault()); + double expected = testMedian(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2); + State median = PersistenceExtensions.medianBetween(energyPriceQuantityItem, beginStored, endStored, SERVICE_ID); + + assertNotNull(median); + QuantityType qt = median.as(QuantityType.class); + assertNotNull(qt); + assertThat(qt.doubleValue(), is(closeTo(expected, 0.01))); + assertEquals(CurrencyUnits.BASE_ENERGY_PRICE, qt.getUnit()); + } + @Test public void testSumSinceDecimalType() { State sum = PersistenceExtensions.sumSince(numberItem, @@ -2413,18 +2546,16 @@ public void testSumSinceQuantityType() { assertNotNull(sum); QuantityType qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals(IntStream.rangeClosed(HISTORIC_START, HISTORIC_END).sum() - + (HISTORIC_END - HISTORIC_START + 1) * KELVIN_OFFSET, qt.doubleValue(), 0.001); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(IntStream.rangeClosed(HISTORIC_START, HISTORIC_END).sum(), qt.doubleValue(), 0.001); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); sum = PersistenceExtensions.sumSince(quantityItem, ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); assertNotNull(sum); qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_END).sum() - + (HISTORIC_END - HISTORIC_INTERMEDIATE_VALUE_1 + 1) * KELVIN_OFFSET, qt.doubleValue(), 0.001); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_END).sum(), qt.doubleValue(), 0.001); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service sum = PersistenceExtensions.sumSince(quantityItem, @@ -2439,9 +2570,8 @@ public void testSumUntilQuantityType() { assertNotNull(sum); QuantityType qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals(IntStream.rangeClosed(FUTURE_START, FUTURE_INTERMEDIATE_VALUE_3).sum() - + (FUTURE_INTERMEDIATE_VALUE_3 - FUTURE_START + 1) * KELVIN_OFFSET, qt.doubleValue(), 0.001); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(IntStream.rangeClosed(FUTURE_START, FUTURE_INTERMEDIATE_VALUE_3).sum(), qt.doubleValue(), 0.001); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service sum = PersistenceExtensions.sumSince(quantityItem, @@ -2451,45 +2581,38 @@ public void testSumUntilQuantityType() { @Test public void testSumBetweenQuantityType() { - State sum = PersistenceExtensions.sumBetween(groupQuantityItem, + State sum = PersistenceExtensions.sumBetween(quantityItem, ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); assertNotNull(sum); QuantityType qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals( - IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2).sum() - + (HISTORIC_INTERMEDIATE_VALUE_2 - HISTORIC_INTERMEDIATE_VALUE_1 + 1) * KELVIN_OFFSET, + assertEquals(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2).sum(), qt.doubleValue(), 0.001); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); - sum = PersistenceExtensions.sumBetween(groupQuantityItem, + sum = PersistenceExtensions.sumBetween(quantityItem, ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_4, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); assertNotNull(sum); qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals( - IntStream.rangeClosed(FUTURE_INTERMEDIATE_VALUE_3, FUTURE_INTERMEDIATE_VALUE_4).sum() - + (FUTURE_INTERMEDIATE_VALUE_4 - FUTURE_INTERMEDIATE_VALUE_3 + 1) * KELVIN_OFFSET, + assertEquals(IntStream.rangeClosed(FUTURE_INTERMEDIATE_VALUE_3, FUTURE_INTERMEDIATE_VALUE_4).sum(), qt.doubleValue(), 0.001); - sum = PersistenceExtensions.sumBetween(groupQuantityItem, + sum = PersistenceExtensions.sumBetween(quantityItem, ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); assertNotNull(sum); qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals(IntStream - .concat(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_END), - IntStream.rangeClosed(FUTURE_START, FUTURE_INTERMEDIATE_VALUE_3)) - .sum() - + (HISTORIC_END - HISTORIC_INTERMEDIATE_VALUE_1 + FUTURE_INTERMEDIATE_VALUE_3 - FUTURE_START + 2) - * KELVIN_OFFSET, + assertEquals( + IntStream.concat(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_END), + IntStream.rangeClosed(FUTURE_START, FUTURE_INTERMEDIATE_VALUE_3)).sum(), qt.doubleValue(), 0.001); // default persistence service - sum = PersistenceExtensions.sumBetween(groupQuantityItem, + sum = PersistenceExtensions.sumBetween(quantityItem, ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault())); @@ -2503,18 +2626,16 @@ public void testSumSinceGroupQuantityType() { assertNotNull(sum); QuantityType qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals(IntStream.rangeClosed(HISTORIC_START, HISTORIC_END).sum() - + (HISTORIC_END - HISTORIC_START + 1) * KELVIN_OFFSET, qt.doubleValue(), 0.001); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(IntStream.rangeClosed(HISTORIC_START, HISTORIC_END).sum(), qt.doubleValue(), 0.001); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); sum = PersistenceExtensions.sumSince(groupQuantityItem, ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); assertNotNull(sum); qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_END).sum() - + (HISTORIC_END - HISTORIC_INTERMEDIATE_VALUE_1 + 1) * KELVIN_OFFSET, qt.doubleValue(), 0.001); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_END).sum(), qt.doubleValue(), 0.001); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service sum = PersistenceExtensions.sumSince(groupQuantityItem, @@ -2529,9 +2650,8 @@ public void testSumUntilGroupQuantityType() { assertNotNull(sum); QuantityType qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals(IntStream.rangeClosed(FUTURE_START, FUTURE_INTERMEDIATE_VALUE_3).sum() - + (FUTURE_INTERMEDIATE_VALUE_3 - FUTURE_START + 1) * KELVIN_OFFSET, qt.doubleValue(), 0.001); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(IntStream.rangeClosed(FUTURE_START, FUTURE_INTERMEDIATE_VALUE_3).sum(), qt.doubleValue(), 0.001); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); // default persistence service sum = PersistenceExtensions.sumSince(groupQuantityItem, @@ -2547,11 +2667,9 @@ public void testSumBetweenGroupQuantityType() { assertNotNull(sum); QuantityType qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals( - IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2).sum() - + (HISTORIC_INTERMEDIATE_VALUE_2 - HISTORIC_INTERMEDIATE_VALUE_1 + 1) * KELVIN_OFFSET, + assertEquals(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2).sum(), qt.doubleValue(), 0.001); - assertEquals(Units.KELVIN, qt.getUnit()); + assertEquals(SIUnits.CELSIUS, qt.getUnit()); sum = PersistenceExtensions.sumBetween(groupQuantityItem, ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), @@ -2559,9 +2677,7 @@ public void testSumBetweenGroupQuantityType() { assertNotNull(sum); qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals( - IntStream.rangeClosed(FUTURE_INTERMEDIATE_VALUE_3, FUTURE_INTERMEDIATE_VALUE_4).sum() - + (FUTURE_INTERMEDIATE_VALUE_4 - FUTURE_INTERMEDIATE_VALUE_3 + 1) * KELVIN_OFFSET, + assertEquals(IntStream.rangeClosed(FUTURE_INTERMEDIATE_VALUE_3, FUTURE_INTERMEDIATE_VALUE_4).sum(), qt.doubleValue(), 0.001); sum = PersistenceExtensions.sumBetween(groupQuantityItem, @@ -2570,12 +2686,9 @@ public void testSumBetweenGroupQuantityType() { assertNotNull(sum); qt = sum.as(QuantityType.class); assertNotNull(qt); - assertEquals(IntStream - .concat(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_END), - IntStream.rangeClosed(FUTURE_START, FUTURE_INTERMEDIATE_VALUE_3)) - .sum() - + (HISTORIC_END - HISTORIC_INTERMEDIATE_VALUE_1 + FUTURE_INTERMEDIATE_VALUE_3 - FUTURE_START + 2) - * KELVIN_OFFSET, + assertEquals( + IntStream.concat(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_END), + IntStream.rangeClosed(FUTURE_START, FUTURE_INTERMEDIATE_VALUE_3)).sum(), qt.doubleValue(), 0.001); // default persistence service @@ -2586,6 +2699,19 @@ public void testSumBetweenGroupQuantityType() { assertNull(sum); } + @Test + public void testSumBetweenEnergyPriceQuantityType() { + State sum = PersistenceExtensions.sumBetween(energyPriceQuantityItem, + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); + assertNotNull(sum); + QuantityType qt = sum.as(QuantityType.class); + assertNotNull(qt); + assertEquals(IntStream.rangeClosed(HISTORIC_INTERMEDIATE_VALUE_1, HISTORIC_INTERMEDIATE_VALUE_2).sum(), + qt.doubleValue(), 0.001); + assertEquals(CurrencyUnits.BASE_ENERGY_PRICE, qt.getUnit()); + } + @Test public void testLastUpdate() { ZonedDateTime lastUpdate = PersistenceExtensions.lastUpdate(numberItem, SERVICE_ID); @@ -2752,6 +2878,15 @@ public void testDeltaBetween() { assertEquals(HISTORIC_INTERMEDIATE_VALUE_2 - HISTORIC_INTERMEDIATE_VALUE_1, qt.doubleValue(), 0.001); assertEquals(SIUnits.CELSIUS, qt.getUnit()); + delta = PersistenceExtensions.deltaBetween(energyPriceQuantityItem, + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); + assertNotNull(delta); + qt = delta.as(QuantityType.class); + assertNotNull(qt); + assertEquals(HISTORIC_INTERMEDIATE_VALUE_2 - HISTORIC_INTERMEDIATE_VALUE_1, qt.doubleValue(), 0.001); + assertEquals(CurrencyUnits.BASE_ENERGY_PRICE, qt.getUnit()); + delta = PersistenceExtensions.deltaBetween(numberItem, ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_4, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); @@ -2883,6 +3018,15 @@ public void testEvolutionRateBetween() { 100.0 * (HISTORIC_INTERMEDIATE_VALUE_2 - HISTORIC_INTERMEDIATE_VALUE_1) / HISTORIC_INTERMEDIATE_VALUE_1, 0.001))); + rate = PersistenceExtensions.evolutionRateBetween(energyPriceQuantityItem, + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), + ZonedDateTime.of(HISTORIC_INTERMEDIATE_VALUE_2, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); + assertNotNull(rate); + // ((now - then) / then) * 100 + assertThat(rate.doubleValue(), is(closeTo( + 100.0 * (HISTORIC_INTERMEDIATE_VALUE_2 - HISTORIC_INTERMEDIATE_VALUE_1) / HISTORIC_INTERMEDIATE_VALUE_1, + 0.001))); + rate = PersistenceExtensions.evolutionRateBetween(numberItem, ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_3, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(FUTURE_INTERMEDIATE_VALUE_4, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), SERVICE_ID); diff --git a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/TestPersistenceService.java b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/TestPersistenceService.java index ff39cdc281a..2196a2d8161 100644 --- a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/TestPersistenceService.java +++ b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/TestPersistenceService.java @@ -51,6 +51,7 @@ * @author Kai Kreuzer - Initial contribution * @author Mark Herwege - Allow future values * @author Mark Herwege - Adapt test expected value logic for Riemann sums + * @author Mark Herwege - use base unit for calculations and results */ @NonNullByDefault public class TestPersistenceService implements QueryablePersistenceService { @@ -90,8 +91,6 @@ public class TestPersistenceService implements QueryablePersistenceService { static final int AFTER_END = BASE_VALUE + 85; // 2110 static final DecimalType STATE = new DecimalType(HISTORIC_END); - static final double KELVIN_OFFSET = 273.15; - private final ItemRegistry itemRegistry; public TestPersistenceService(ItemRegistry itemRegistry) { @@ -241,33 +240,20 @@ static OnOffType switchValue(int hour) { } static DecimalType value(long year) { - return value(year, false); - } - - private static DecimalType value(long year, boolean kelvinOffset) { if (year < HISTORIC_START) { return DecimalType.ZERO; } else if (year <= HISTORIC_END) { - return new DecimalType(year + (kelvinOffset ? KELVIN_OFFSET : 0)); + return new DecimalType(year); } else if (year < FUTURE_START) { - return new DecimalType(HISTORIC_END + (kelvinOffset ? KELVIN_OFFSET : 0)); + return new DecimalType(HISTORIC_END); } else if (year <= FUTURE_END) { - return new DecimalType(year + (kelvinOffset ? KELVIN_OFFSET : 0)); + return new DecimalType(year); } else { - return new DecimalType(FUTURE_END + (kelvinOffset ? KELVIN_OFFSET : 0)); + return new DecimalType(FUTURE_END); } } static double testRiemannSum(@Nullable Integer beginYear, @Nullable Integer endYear, RiemannType type) { - return testRiemannSum(beginYear, endYear, type, false); - } - - static double testRiemannSumCelsius(@Nullable Integer beginYear, @Nullable Integer endYear, RiemannType type) { - return testRiemannSum(beginYear, endYear, type, true); - } - - private static double testRiemannSum(@Nullable Integer beginYear, @Nullable Integer endYear, RiemannType type, - boolean kelvinOffset) { ZonedDateTime now = ZonedDateTime.now(); int begin = beginYear != null ? (beginYear < HISTORIC_START ? HISTORIC_START : beginYear) : now.getYear() + 1; int end = endYear != null ? endYear : now.getYear(); @@ -284,7 +270,7 @@ private static double testRiemannSum(@Nullable Integer beginYear, @Nullable Inte } while (index < end) { int bucketStart = index; - double value = value(index, kelvinOffset).doubleValue(); + double value = value(index).doubleValue(); while ((index < end - 1) && (value(index).longValue() == value(index + 1).longValue())) { index++; } @@ -314,7 +300,7 @@ private static double testRiemannSum(@Nullable Integer beginYear, @Nullable Inte index++; } index++; - double value = value(index, kelvinOffset).doubleValue(); + double value = value(index).doubleValue(); duration += Duration .between(ZonedDateTime.of(bucketStart, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(index, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault())) @@ -336,12 +322,12 @@ private static double testRiemannSum(@Nullable Integer beginYear, @Nullable Inte } while (index < end) { int bucketStart = index; - double value = value(index, kelvinOffset).doubleValue(); + double value = value(index).doubleValue(); while ((index < end - 1) && (value(index).longValue() == value(index + 1).longValue())) { index++; } index++; - value = (value + value(index, kelvinOffset).doubleValue()) / 2.0; + value = (value + value(index).doubleValue()) / 2.0; duration += Duration .between(ZonedDateTime.of(bucketStart, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(index, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault())) @@ -358,7 +344,7 @@ private static double testRiemannSum(@Nullable Integer beginYear, @Nullable Inte case MIDPOINT: int nextIndex = begin; boolean startBucket = true; - double startValue = value(begin, kelvinOffset).doubleValue(); + double startValue = value(begin).doubleValue(); if (beginYear == null) { duration = Duration.between(now, ZonedDateTime.of(begin, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault())) .toSeconds(); @@ -369,7 +355,7 @@ private static double testRiemannSum(@Nullable Integer beginYear, @Nullable Inte index++; } index++; - double value = value(index, kelvinOffset).doubleValue(); + double value = value(index).doubleValue(); duration += Duration .between(ZonedDateTime.of(bucketStart, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), ZonedDateTime.of(index, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault())) @@ -397,7 +383,7 @@ private static double testRiemannSum(@Nullable Integer beginYear, @Nullable Inte sum += value * (duration + nextDuration) / 2.0; duration = 0; } - double endValue = value(end, kelvinOffset).doubleValue(); + double endValue = value(end).doubleValue(); long endDuration = nextDuration; sum += endValue * endDuration / 2.0; break;