Cast state to number keeps the unit

I’m trying to do a simple comparison of a temperature item with a value, something that seems to cause a lot of trouble.

var outdoor_temp=NetatmoOutdoor_Temperature.state as Number
logInfo("IANR","Triggered1 " + outdoor_temp)

The log output is;

Triggered1 8.80000019073486328125 °C

How do I cast the state to a number, without any units so I can use it in a numerical comparison? I’ve rattled around various topics and this is written up as a solution in many of them, in others I’ve seen a line of casts to strings then parsing those strings to floats, I can’t believe something so simple and frequently needed should be so messy so surely there’s a simple way to do this?

Previously I’ve done it this way but even this seems messy, is there not a more elegant, simple, easy-to-remember way to do this?

currentstate=(radiator.state as QuantityType<Temperature>).doubleValue

OpenHab Version: 2.5.0~S1490-1
Platform: Debian GNU/Linux 9 (stretch)
Hardware: VM, 2 CPUs, 3GB RAM, 20 GB disc, 15 GB free
java version “1.8.0_202”
Java™ SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot™ 64-Bit Server VM (build 25.202-b08, mixed mode)

No.

When I cast to Number, should this not remove the units, or is that just a “hint” to the rules engine about the type of data to expect?

the latter.

When you cast the state to Number, you are not actually changing the underlying Object. You are just telling the language that “This state is, among other things, a Number so I’m going to treat it as a Number in this Rule.” So when you call toString, you are still calling the same toString method implemented by the state object. Since the state object is a QuantityType, you will get the unit as part of the toString.

OK thanks for the clarification. I’ll stick to the quantitytype doubleValue conversion then as that seems to be the one that works for me even if I never seem to remember it until I check my notes!

1 Like

Try this:

var outdoor_temp = (NetatmoOutdoor_Temperature.state as Number).floatValue
logInfo("IANR","Triggered1 " + outdoor_temp)

1 Like

I am using for a comparison this:
if((Outd_Temp.state as Number) < (Ind_Temp.state as Number))
(also Netatmo)
This seems to work.
I guess that’s because the units are taken into account!?

In other cases I use:
if(Ind_Temp.state > 25 |"°C") {

I think your comparison-methods are both ok, depending on the Item-Types you’re comparing.

Your fist example ** if((Outd_Temp.state as Number) < (Ind_Temp.state as Number))** compares two Number-Items.
In the second example you’re comparing a Quantity-Type (UoM) Item with a constant with the same UoM ( "°C")

But you also can do this:

if ((Ind_Temp.state as Number).floatValue > 25 ) {

which will eliminate the Unit in your Item, no matter if it’s °C, km/h, % or whatever. So this will make both sides of your comparison equal.

But note that comparing one value in °C with another in, say, °F will work properly if you stay within the UoM framework.
20 °C > 32 °F .

Once you extract “just the number” that’s what you get, just the number.
20 < 32

1 Like

@fibu-freak
Thanks for pointing out the difference.
I actually got used to the |"°C" stuff finally and @rossko57 point is an interesting point.
(even though I live in Germany, so using °F is out of the question.)

Yeh, but kW / W comparisons and so on come up.

The bit I’ve never figured out, but surely must exist, is to extract a value from a UoM Item in a designated unit.
e.g. from a Number:Power, which might be in W or mW or kW, get the state expressed in W
That’s easy for display using label [state presentation] like [%d W] and the framework sorts it out for you - but not in a rule that I’ve found.
var x = myvalue.asQuantityTypeUsing("W") or suchlike.

I think the example of @rossko57 is very interesting also in other cases where you’re using UoM and comparing.

speed: km/h <-> mp/h
length: m <-> in

etc.:look here

Number:Temperature    Test_UoM_Temperature    "Temperature [%.0f %unit%]"
Number:Length         Test_UoM_Length         "Length [%.0f %unit%]"
Number:Power          Test_UoM_Power          "Power [%.0f W]"
//Number:Power          Test_UoM_Power          "Power [%.0f %unit%]"

I haven’t tried this in the rules DSL, but it looks like it would be possible…

from time import sleep

from core.log import logging, LOG_PREFIX#, log_traceback

LOG = logging.getLogger("{}.TEST".format(LOG_PREFIX))

events.sendCommand("Test_UoM_Temperature", "212")
events.sendCommand("Test_UoM_Length", "55555")
events.sendCommand("Test_UoM_Power", "55555")
sleep(1)# wait for event bus

LOG.warn("")
LOG.warn(u"Test_UoM_Temperature: {}".format(ir.getItem("Test_UoM_Temperature").toString()))
LOG.warn(u"Test_UoM_Temperature.state: {}".format(items["Test_UoM_Temperature"]))
LOG.warn(u"Test_UoM_Temperature.toUnit(FAHRENHEIT): {}".format(items["Test_UoM_Temperature"].toUnit(ImperialUnits.FAHRENHEIT)))
LOG.warn(u"Test_UoM_Temperature.toUnit(CELSIUS): {}".format(items["Test_UoM_Temperature"].toUnit(SIUnits.CELSIUS)))
LOG.warn("")
LOG.warn(u"Test_UoM_Length: {}".format(ir.getItem("Test_UoM_Length").toString()))
LOG.warn(u"Test_UoM_Length.state: {}".format(items["Test_UoM_Length"]))
LOG.warn(u"Test_UoM_Length.toUnit(FOOT)): {}".format(items["Test_UoM_Length"].toUnit(ImperialUnits.FOOT)))
LOG.warn(u"Test_UoM_Length.toUnit(MILE)): {}".format(items["Test_UoM_Length"].toUnit(ImperialUnits.MILE)))
LOG.warn(u"Test_UoM_Length.toUnit(KILO(METRE)): {}".format(items["Test_UoM_Length"].toUnit(MetricPrefix.KILO(SIUnits.METRE))))
LOG.warn(u"Test_UoM_Length.toUnit(CENTI(METRE)): {}".format(items["Test_UoM_Length"].toUnit(MetricPrefix.CENTI(SIUnits.METRE))))
LOG.warn(u"Test_UoM_Length.toUnit(MILLI(METRE)): {}".format(items["Test_UoM_Length"].toUnit(MetricPrefix.MILLI(SIUnits.METRE))))
LOG.warn("")
LOG.warn(u"Test_UoM_Power: {}".format(ir.getItem("Test_UoM_Power").toString()))
LOG.warn(u"Test_UoM_Power.state: {}".format(items["Test_UoM_Power"]))
LOG.warn(u"Test_UoM_Power.toUnit(MILLI(WATT)): {}".format(items["Test_UoM_Power"].toUnit(MetricPrefix.MILLI(SmartHomeUnits.WATT))))
LOG.warn(u"Test_UoM_Power.toUnit(KILO(WATT)): {}".format(items["Test_UoM_Power"].toUnit(MetricPrefix.KILO(SmartHomeUnits.WATT))))
LOG.warn("")
2020-09-01 16:57:42.537 [WARN ] [jython.TEST] - 
2020-09-01 16:57:42.538 [WARN ] [jython.TEST] - Test_UoM_Temperature: Test_UoM_Temperature (Type=NumberItem, State=212.0 °F, Label=Temperature, Category=null)
2020-09-01 16:57:42.538 [WARN ] [jython.TEST] - Test_UoM_Temperature.state: 212.0 °F
2020-09-01 16:57:42.539 [WARN ] [jython.TEST] - Test_UoM_Temperature.toUnit(FAHRENHEIT): 212.0 °F
2020-09-01 16:57:42.539 [WARN ] [jython.TEST] - Test_UoM_Temperature.toUnit(CELSIUS): 100.00 °C
2020-09-01 16:57:42.540 [WARN ] [jython.TEST] - 
2020-09-01 16:57:42.540 [WARN ] [jython.TEST] - Test_UoM_Length: Test_UoM_Length (Type=NumberItem, State=55555.0 in, Label=Length, Category=null)
2020-09-01 16:57:42.540 [WARN ] [jython.TEST] - Test_UoM_Length.state: 55555.0 in
2020-09-01 16:57:42.541 [WARN ] [jython.TEST] - Test_UoM_Length.toUnit(FOOT)): 4629.58333333333314815000 ft
2020-09-01 16:57:42.541 [WARN ] [jython.TEST] - Test_UoM_Length.toUnit(MILE)): 0.8768150252525251578292297979797974 mi
2020-09-01 16:57:42.542 [WARN ] [jython.TEST] - Test_UoM_Length.toUnit(KILO(METRE)): 1.411097 km
2020-09-01 16:57:42.542 [WARN ] [jython.TEST] - Test_UoM_Length.toUnit(CENTI(METRE)): 141109.7 cm
2020-09-01 16:57:42.543 [WARN ] [jython.TEST] - Test_UoM_Length.toUnit(MILLI(METRE)): 1411097.0 mm
2020-09-01 16:57:42.543 [WARN ] [jython.TEST] - 
2020-09-01 16:57:42.544 [WARN ] [jython.TEST] - Test_UoM_Power: Test_UoM_Power (Type=NumberItem, State=55555.0 W, Label=null, Category=null)
2020-09-01 16:57:42.544 [WARN ] [jython.TEST] - Test_UoM_Power.state: 55555.0 W
2020-09-01 16:57:42.545 [WARN ] [jython.TEST] - Test_UoM_Power.toUnit(MILLI(WATT)): 55555000.0 mW
2020-09-01 16:57:42.545 [WARN ] [jython.TEST] - Test_UoM_Power.toUnit(KILO(WATT)): 55.555 kW
2020-09-01 16:57:42.545 [WARN ] [jython.TEST] - 

Ready for a really neat bug?! Take note of the commented out Test_UoM_Power Item above… it won’t work! When using the default unit in the label, some Items using UoM (e.g. Number:Power, Number:Energy, etc.) are not created correctly and their states are DecimalType. If you use a specific unit in the label or no label at all, the state correctly shows up as a QuantityType. I don’t recall a bug being reported or fixed for this, so I will get an issue opened when I’m done poking at it.

3 Likes

Inspired me to have another crack at DSL.
After a bit of a play, got .toUnit() working in DSL rules.

The trick is that myItem.state is a state type object which has no toUnit() method; so first you must get hold of it as a QuantityType.

testTemp is a Number:Temperature type Item here

logInfo("test", "raw state " + testTemp.state.toString)

   // get our value as a Temperature Quantity
   // VSCode editor will complain about <Temperature>, but it works
//var qtTemp = testTemp.state as QuantityType<Temperature>
   // EDIT - that's a misuse, this the way
var qtTemp = testTemp.state as QuantityType<Number>

logInfo("test", "qt raw " + qtTemp.toString)
   // now get in different units
var qtF = qtTemp.toUnit("°F")
logInfo("test", "conv to F " + qtF.toString)

   // now get in different units
var qtK = qtTemp.toUnit("K")
logInfo("test", "conv to K " + qtK.toString)

   // now do sums in mixed units
var sums = qtF + qtK + 10 | °C
   // results will come in the units of first argument
logInfo("test", "maths F first " + sums.toString)
sums = 10 | °C + qtF + qtK
logInfo("test", "maths C first " + sums.toString)
2020-09-02 01:07:52.441 [INFO ] [.eclipse.smarthome.model.script.test] - raw state 11.24 °C
2020-09-02 01:07:52.443 [INFO ] [.eclipse.smarthome.model.script.test] - qt raw 11.24 °C
2020-09-02 01:07:52.444 [INFO ] [.eclipse.smarthome.model.script.test] - conv to F 52.232 °F
2020-09-02 01:07:52.446 [INFO ] [.eclipse.smarthome.model.script.test] - conv to K 284.39 K
2020-09-02 01:07:52.448 [INFO ] [.eclipse.smarthome.model.script.test] - maths F first 154.464 °F
2020-09-02 01:07:52.450 [INFO ] [.eclipse.smarthome.model.script.test] - maths C first 32.480 °C

So the answer to my own question (for DSL) -

is

var myQTvariable  =  (myItem.state as QuantityType<Number>).toUnits("givenUnit")

where you can find the exact units string (“symbol”) for that type from -

and the prefixes like “W”, “mW”, “kW” etc. will be handled correctly without further fuss…

EDIT - Misuse of QuantityType<Temperature> on my part; in fact use QuantityType<Number> because DSL variable type Number is a Quantity Type already (not be confused with Item type Number).
That’s where this thread began.

The trap here is that Number variable object doesn’t have a .toUnit() while QuantityType<Number> does.

1 Like

I got curious! These work…

    logWarn("Rules", "Test_UoM_Temperature.getStateAs(QuantityType).toUnit(CELSIUS): {}", Test_UoM_Temperature.getStateAs(QuantityType).toUnit(CELSIUS))
    logWarn("Rules", "Test_UoM_Length.getStateAs(QuantityType).toUnit(KILO(METRE): {}", Test_UoM_Length.getStateAs(QuantityType).toUnit(KILO(METRE)))

But these don’t…

    logWarn("Rules", "Test_UoM_Temperature.getStateAs(QuantityType).toUnit(CELSIUS): {}", Test_UoM_Temperature.getStateAs(QuantityType).toUnit(CELSIUS))
    logWarn("Rules", "Test_UoM_Length.getStateAs(QuantityType).toUnit(KILO(METRE): {}", Test_UoM_Length.getStateAs(QuantityType).toUnit(KILO(METRE)))

I think it may be related to the issue I mentioned before. Also, validation kicks out some warnings before the error comes out…

2020-09-01 23:37:41.660 [INFO ] [org.eclipse.smarthome.model.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'Test.rules', using it anyway:
The field SIUnits.CELSIUS refers to the missing type Object
The field SIUnits.METRE refers to the missing type Object
The field SmartHomeUnits.WATT refers to the missing type Object
The field SmartHomeUnits.WATT refers to the missing type Object
2020-09-01 23:37:41.681 [INFO ] [org.eclipse.smarthome.model.core.internal.ModelRepositoryImpl] - Refreshing model 'Test.rules'
2020-09-01 23:37:46.768 [DEBUG] [org.eclipse.smarthome.model.rule.runtime.internal.engine.RuleEngineImpl] - Executing startup rule 'Test DSL rule'
2020-09-01 23:37:46.934 [ERROR] [org.eclipse.smarthome.model.rule.runtime.internal.engine.RuleEngineImpl] - Error during the execution of startup rule 'Test DSL rule': cannot invoke method public org.eclipse.smarthome.core.library.types.QuantityType org.eclipse.smarthome.core.library.types.QuantityType.toUnit(javax.measure.Unit) on null

I did not try importing SIUnits or SmartHomeUnits.