Doing maths on Temperature Quantities is a bit fraught. The temperature type Quantity represents an absolute value, that is to say 1°C represents a particular temperature, not a difference in temperature.
From that viewpoint, 2°C + 2°C makes no sense. Humans can glance at that and think “oh yes, that’s 2°C on the thermometer, plus another 2 graduations makes 4°C” but that’s not so obvious to a computer. The same collection of symbols “2°C” carries two different meanings here.
It’s a bit weird, but consider, 2°C is the same as 275K.
275K + 275K is therefore an identical sum, but now what’s the answer?
Depending on context, 2°C is the same as 2K as well.
We need the + function to be some special thing that works all this out in the specific context of temperatures. So we need to get our starting object as a Quantity object, because that’s the thing that knows what to do.
Note that someItem.state
is a state type object, and will not do.
var tempOffset = (CurrentTemperature.state as QuantityType<Temperature>) + 2|°C
// the pipe thing | makes a Quantity type constant
Having said that, I think the handling of temperatures might be buggy. This ought to work with mixed units … but e.g.
logInfo("sums", "constants {}", 23|°C + 2|K) // result -248.15 °C
logInfo("sums", "constant {}", 2|K + 23|°C) // result 298.15 K
logInfo("sums", "constants {}", 23|°C - 2|K) // result 294.15 °C
but it does not seem to work as expected at OH 2.5.11.
It looks like it takes the second Quantity as the base value, uses the first Quantity as an offset, but returns the value in the units of the offset not the base.
EDIT - added the subtraction test case, I’ve no idea now!
Can anyone confirm this behaviour continues in OH3?
One way to do reliable general maths is to extract the Quantity value in the units of your choice, do maths on pure numbers where you can now predict the units, and if you wish convert back to Quantity afterwards.
We need the to choose the units, because we don’t know what units the Quantity is in to begin with.
“extract the Quantity value in the units of your choice” is the non obvious part.
var tempInC = (CurrentTemperature.state as QuantityType<Temperature>).toUnit("°C") // returns Quantity in units specified
var tempNumeric = (CurrentTemperature.state as QuantityType<Temperature>).toUnit("°C").toBigDecimal // returns number only
Now we don’t care what units our sensor reports in, we can get the numeric part only of the Quantity and know it would be correctly in °C even if sensor reports °F. OH Framework has done the conversion for us.
Putting it all together …
// applies offset to Item in any units
// get the unknown units of the Item
val targetUnit = (tempItem.state as QuantityType<Temperature>).getUnit
// force into known units for offset to avoid weird bug
val offsetTemp = 1|°C + (tempItem.state as QuantityType<Temperature>).toUnit("°C")
// that's a Quantity in C, but we can return to original units
tempItem.postUpdate(offsetTemp.toUnit(targetUnit))