Working with Number:Temperature Items in rules

I do not think so - I copied the character from log, so it should be it

As a beginner I am not shure about the use of units.
I try to integrate a Powerwall and an inverter of different companies (Tesla Powerwall, Fronius Symo):
Using your example:

logInfo("PW", "PW_Haus: " + PW_Home.state.toString)
logInfo("FSY", "FSY_Load: " + FroniusSymoInverter_LoadPower.toString)

gives

2021-02-28 19:56:25.405 [INFO ] [org.openhab.core.model.script.PW    ] - PW_Haus: 0.30129491788679763 kW

2021-02-28 19:56:25.424 [INFO ] [org.openhab.core.model.script.FSY   ] - FSY_Load: FroniusSymoInverter_LoadPower (Type=NumberItem, State=-11.78, Label=FSY Load Power, Category=, Tags=[Point])

So: If I want to work with these I have to convert both to numbers, multiplying the PW power by 1000?

home
load
I understand the output of PW_Home in the UI with unit kW, how does the UI know that
FroniusSymoInverter_LoadPower hast to be display with the unit W?

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.

that would rather be var numericC = tempItemC.toBigDecimal, wouldn’t it ?
(yes it works and actually I wonder why the parser does not complain about unnecessary conversion, but still…)

On a sidenote, the following code is giving different values for t4,t5,t6. Not sure why and if that is a bug. Physics put aside, t3 is shown to be K² .

    var t = 20|°C
    var t2 = 68|°F
    var t3 = t2 * t

    var t4 = t3 / t2.toUnit("°C")
    var t5 = t3 / t2.toUnit("°F")
    var t6 = t3 / t2.toUnit("K")

Um, I suppose it depends what your tempItemC object is exactly. In my examples above, it was an Item, so we looked at tempItemC.state, which doesn’t have a .toBigDecimal method (in OH2 at least).

The “weird maths” is fun.
With numbers, for readers;

    var t = 20|°C
    var t2 = 68|°F
    var t3 = t2 * t  // result 1360 °C·°F

    var t4 = t3 / t2.toUnit("°C")  // result 68 °F
    var t5 = t3 / t2.toUnit("°F")  // result 20 °C
    var t6 = t3 / t2.toUnit("K")  // 4.639263175848541702200238785604639 °C·°F/K

1360 °C·°F gives me a headache. What does that mean, what does it represent? It is analogous to the example multiplication that gives °C² units, that is equally beyond my grasp.

Anyway, weird results are I think just like the temperature addition problem - different unit systems have different zero points, different points of origin.
The absolute vs. increment issue.
Does “1°C” represent the difference between 1°C and 2°C, and so 1.8°F is “the same”, or does it represent the temperature 1°C, and so 34°F is “the same”.
This really matters when we start mixing units and have to make conversions.

There’s no way to signal openHAB which you mean in any given case. “1°C” is just ambiguous in meaning.
Apparently “wrong” answers are more likely unexpected assumptions.

Let’s work some simpler examples, and dodge the temperature-squared headache.

    var t = 20|°C
        // simple division
    var t4 = t / 2  // result 10 °C
    var t5 = t.toUnit("°F") / 2  // result 34.00 °F, that's equivalent 1.1°C

This gives us clues about how the framework is going to work this.
“Half of 20°C” seems a simple enough calculation, but it is ambiguous.
Should we expect “Half of 68°F” to give the same answer? If not, why not? Starting conditions are the same.

In reality, the framework has simply assumed that dividing-a-Quantity-by-a-number means you want it to divide the numeric part only and then give the result in same units you started with.
That works fine with metres and inches, which have a common meaning for zero.
It might not be what you expected for temperatures or db/percentage, which have different zeroes and scales.

    var t = 20|°C
    var t2 = 10|°C
        // divide quantity by quantity
    var t4 = t / t2  // result 2
    var t5 = t.toUnit("°F") / t2  // result 6.80 °F/°C
    var t6 = t.toUnit("°F") / t2toUnit("°F")  // result 1.36

So now the question is “How many times does 10°C go into 20°C?”
Seems simple, but to a scientist it is ambiguous. 20°C = 293K so 10°C or 10K will “go” 29 and a bit times.

Framework has taken the easy option, “20°C divide by 10°C, just divide the numeric part only and the units cancel out, °C/°C is unity”.

“How many times does 10°C go into 68°F?” has apparently flummoxed the framework. It’s actually done the same thing really, “divide numeric part only, and the units … err … well look, here is what you asked for, expressed in units of °F/°C”
It’s a mathematically correct answer.

For “How many times does 50°F go into 68°F?” the framework has been consistent and applied the same rule, giving 1.36 and no units (°F/°F)

I think it is going to apply these same basic approaches to your temperature-squared calculations, which may be different from the assumptions humans make.

Yeah I don’t grasp it either, maybe some more intelligent Ph.D. than me does, but see it as just an intermediary result, part of a greater calculation.

So let’s consolidate that into one line to have even more fun:

    var t8 = t2 * t / t2                                       // 20 K
    var t9 = t2 * t.toUnit(t2.getUnit) / t2                    // 37.77777777777777777777777777777778 K
    var t10 = t2.toUnit(t.getUnit) * t / t2.toUnit(t.getUnit)  // 19.9999999999999999999999999999999999999999999999999999999999999999800 K
    var t11 = t2.toUnit(t.getUnit) * t / t2                    // 10.5882352941176470588235294117647145882352941176470588235294117647040 K

Now all of them are in K, which hasn’t been involved as a unit ?
Not a single one to have the obvious answer, t = 20 °C that is.

To be fair, you didn’t ask for any particular unit … did you want the system to make an assumption :wink:

One of the difficulties handling variables in rules is that we cannot assign a variable a ‘default unit’, so they don’t behave quite the same as Item states. We can specify a unit when loading any particular value into a variable, but unlike a state we can’t have it converted to some other desired unit as part of the update. Feels relevant here.

Now, here’s the thing … my results

    var t = 20|°C
    var t2 = 10|°C

    var t8 = t2 * t / t2     // result 20 °C
    var t9 = t2 * t.toUnit(t2.getUnit) / t2    // result 20 °C
    var t10 = t2.toUnit(t.getUnit) * t / t2.toUnit(t.getUnit)  // result 20 °C
    var t11 = t2.toUnit(t.getUnit) * t / t2   // result 20 °C

which is perhaps what you expected.

I’m different because that’s in OH2 … UoM had an overhaul for OH3.1

and it’s changed some of the built-in assumptions.

So, is the answer to t2 * t / t2 20°C or 20K ??

Well, I think the answer is “yes”, because the question is ambiguous.
20°C / 10°C is ambiguous. We can represent it as 20K / 10K or as 293K / 283K.
Did you mean this temperature difference divided by that temperature difference? Or did you mean this absolute temperature divided by that absolute temperature?
Did you even mean one of each … this absolute temperature divided by that temperature difference, 293K / 10K ?
These are all valid interpretations.

I think what you’ve discovered is the absolute/difference assumptions are now more complicated and depending on context.

You might try these variations

var t12 = (t2 * t) / t2    // OH2 result  20 °C
var t13 = t2 * (t / t2)    // OH2 result  20 °C

I suspect when used in the .toUnit() cases, this might give insight into the unexpected results

I didn’t repeat this but I also had

var t = 20|°C
var t2 = 68|°F

So t2 * t / t2 equals t which has a well-defined unit, °C that is. Yet the system chose K.

So to be fair as well, you meanwhile have been cheating a little bit by setting both t2 AND t to °C so you effectively created the unambiguous unit.
Then again you’re right in that it changed in OH3, I always get K even if t and t2 have the same unit.

Oh come on …you didn’t tell us what you used second time. :smiley:
There’s no cheating, there’s just ambiguous notation and unexpected results.
Anyway, easy enough to repeat the exercise. OH2-with-old-UoM-library still differs from your K results.

    var t = 20|°C
    var t2 = 68|°F

    var t8 = t2 * t / t2     // result 20 °C
    var t9 = t2 * t.toUnit(t2.getUnit) / t2    // result 68.00 °F
    var t10 = t2.toUnit(t.getUnit) * t / t2.toUnit(t.getUnit)  // result 20 °C
    var t11 = t2.toUnit(t.getUnit) * t / t2   // result 5.882352941176470588235294117647059 °C²/°F

t11 is :crazy_face:

This can be duplicated with constants, for better clarity

    var t8 = 68|°F * 20|°C / 68|°F     // result 20 °C
    var t9 = 68|°F * 68|°F / 68|°F    // result 68.00 °F
    var t10 = 20|°C * 20|°C / 20|°C  // result 20 °C
    var t11 = 20|°C * 20|°C / 68|°F   // result 5.882352941176470588235294117647059 °C²/°F

Unsurprisingly, t9 and t10 confirm that sticking to one unit “works”.

Is t8 “working” a mystery, then?
I’ll make the assumption that auto-conversion for mixed units is not applied in the divide case (in OH2).
We can test that -

var divpart = 20|°C / 68|°F  // result 0.2941176470588235294117647058823529 °C/°F

Yep, it’s just done maths on the numeric parts and made up a unit.
Hmm, curious now …

var divsame = 68|°F / 68|°F  // result 1

Aha - at some point in the processing the framework is smart enough to realize that °F/°F is a null unit and discard it.
While °C/°F is not null and is retained.
Here’s the difference we’ve been looking for I reckon.

What about multiply?

var multpart = 20|°C * 68|°F // result 1360 °C·°F
var square = 20|°C * 20|°C // result 400 °C²

Just as daft units, but the point is that we will always get a unit of some kind.
But now we can see what’s going on, if we fill partial results.

        // we can sneak weird units past the validator as a string
    var t8p = 68|°F * 0.2941176470588235294117647058823529 | "°C/°F"     // result 20.00000000000000000000000000000000 °C
    var t11p = 20|°C * 0.2941176470588235294117647058823529 | "°C/°F"   // result 5.882352941176470588235294117647058 °C²/°F

So the t8p result is “good”. It’s not identical to the “full” t8 calculation, with all those trailing zeros; perhaps just differences in number type handling when parsed.

And the t11p result can be understood. It’s just done simple maths with the numerics, no conversions, same as t8p.
But t8p units would be °F * °C/°F - framework is smart enough to cancel °°F/°F leaving just °C.
t11p units would be °C * °C/°F - no cancelling can be done, leaving °C²/°F

In summary, all mathematically correct, but probably not useful. 5.88°C²/°F is a “correct” answer but not in units we can use.
Maybe this is why UoM library has been updated.

OH2 users can manage all this in real life by following the golden rule “use the same units throughout”

That’s good info.
Maybe 3.1 UoM has a new policy of sorting out conflicting units in some ‘base’ unit, which would reasonably be K. Note that t/t2 has ‘unit/unit’ dimension if they are expressed in the same units, so t * t/t2 represents a conflict between t with unit and divide with unit/unit.

But your K results suggest it is making a right mess of this policy here.

1 Like

How do I specify a speed unit in km/h? When I try: if (speedItem.state > 5|km/h) I get the following error:

Script execution of rule with UID ‘somfy-4’ failed: The name ‘h’ cannot be resolved to an item or type; line 222, column 129, length 1 in somfy

Is there a way to cast the unit out? I’ve also tried using if ((speedItem.state as Number) > 5), but it does not seem to work either…

Maybe you have to escape the ‘/’ ?
if (speedItem.state > 5|km\/h)

Just throwing the idea …

/ is a maths operator, so we’ll have to hide it from the rules interpreter.

if ( speedItem.state > 5 | "km/h" ) 

There are methods shown in the first post to
extract the numeric part in whatever-units-it-come-in
extract the numeric part in some-unit-you-designate
extract the unit symbol

Heads up, openHAB will have concept of relative unit conversion Add QuantityType.toUnitRelative by ccutrer · Pull Request #3177 · openhab/openhab-core · GitHub that should help dealing with temperature units

openhab 3.3
openhab-js

I have the following Power Items defined:

Number:Power    El_GenPower     "PV Power"   (El_Generation)   ["Measurement","Power"]  
Number:Power  El_GridPower      "Active Grid Power"   (El_Grid)  ["Measurement","Power"]                             
    {channel="homewizard:p1_wifi_meter:__P1:active_power" , stateDescription="PG" [pattern="%.0f %unit%"]}
Number:Power  El_ActiveLoad "Active Load"   (El_Load) ["Measurement","Power"]                                 
    {stateDescription="PL" [pattern="%.0f %unit%"]}

El_GenPower is set by a rule and that works
El_GridPower is linked to a channel (of my smart power meter)

I want to determine El_ActiveLoad as the sum of the two other Powers, using a rule in openhab-js, where I do the following calculation in a ECMA-2021 script:

var load = ( (items.getItem('El_GridPower').state as QuantityType<Power>) + (items.getItem('El_GenPower').state as QuantityType<Power>) ) ;

items.getItem("El_ActiveLoad").postUpdate(load);

why does the variable definition and calculation fail with:

org.graalvm.polyglot.PolyglotException: SyntaxError: <eval>:5:50 Expected ) but found as
var load = ( (items.getItem('El_GridPower').state as QuantityType<Power>) + (items.getItem('El_GenPower').state as QuantityType<Power>) ) ;
                                                  ^

Per the reference docs for the JS Scripting Add-on (which should always be the first place you look) JavaScript Scripting - Automation | openHAB, items.getItem() returns a JavaScript Item Object. The JavaScript Item Object’s .state is a String.

But even if it were JavaScript doesn’t have an as operator and doesn’t really do casting. If it did, it’s certainly not going to understand the QuantityType<Power> notation which is a Java syntax. Finally, JavaScript doesn’t support operator overloading so you’ll never be able to just use + to do math with a QuantityType. you’ll have to use the .add() method to add them together.

For this old of an OH version, something like this might work OK.

var load = items.getItem('El_GridPower').rawState.add(items.getItem('El_GenPower').rawState);
items.getItem('El_ActiveLoad').postUpdate(load.toString()); // sendCommand and postUpdate always takes a string

In the most recent version of openhab-js, native JS support for QuantityTypes using the Quantity library has been provided. So something like this would work:

var load = items.El_GridPower.quantityState.add(items.El_GenPower.quantityState);
items.El_ActiveLoad.postUpdate(load.toString());

Continuing the discussion from Working with Number:Temperature Items in rules:

I read a few times the first post but am still struggeling with the math topic in rules:

I tried to do a calculation similar to this one:

var avg = ( (tempItemC.state as QuantityType<Temperature>) + (tempItemF.state as QuantityType<Temperature>) ) / 2 
 // returns 1.66666666666666666666666666666665 °C

My items are:

Number:Temperature		NE4_Wohnen_Temperatur			"Temperatur" 		<temperature> 		(NE4_WohnenEssen, gActpointN)	{channel="knx:device:bridge:EGN_NE4w_Taster:Temperatur", unit="°C", stateDescription=""[pattern="%.1f %unit%"]}
Number:Temperature		NO1_Bad_Temperatur				"Temperatur"  		<temperature>       (NO1_Bad, gActpointN)			{channel="knx:device:bridge:OGN_NO1_Taster:Temperatur", unit="°C", stateDescription=""[pattern="%.1f %unit%"]}

My rule:

    var avgC = ( (NE4_Wohnen_Temperatur.state as QuantityType<Temperature>) + (NO1_Bad_Temperatur.state as QuantityType<Temperature>) ) / 2
    logInfo("default.rules","Temperatur: " + avgC)

The current values of the temperatures are 18.5 °C and 18.8 °C.
The result of this simple average is “-117.925 °C”.
What am I doing wrong?

on openHAB 4.x? See Adjust QuantityType calculations for temperatures by mherwege · Pull Request #3792 · openhab/openhab-core · GitHub

@rossko57 the original post may need to be updated to avoid confusion to future readers

1 Like

Thanks for the info!

Converting to Kelvin and Converting back to degree after calculation is working fine :slight_smile:

    var avgC = (( (NE4_Wohnen_Temperatur.state as QuantityType<Temperature>).toUnit("K") + (NO1_Bad_Temperatur.state as QuantityType<Temperature>).toUnit("K") ) / 2).toUnit("°C")
    logInfo("default.rules","Temperatur: " + avgC)