Working with Number:Temperature item type

I recently started using the Wunderground binding, which exposes things with the type of Number:Temperature. PaperUI and HABmin both filter the items to which I can link these things so that only items of the Number:Temperature type are available. That’s not a problem in itself, and the linking works just fine.

I can see the the Number:Temperature has the benefit of applying a specific format whenever it’s converted to string on the UI. That makes sense. The problem comes when I try to make comparisons within a rule. I don’t see a documented way to add a variable (or literal) to a rule which matches the state of the Number:Temperature, so I am trying to compare it with float types. I have tried various conversions, but none give the expected result. For example:

var float _maxOutsideTemp = 23.0
...
if ((Weather_MaxTemp_0.state as Number) < _maxOutsideTemp)
{
    logInfo("my.rules", "max temp (" + (Weather_MaxTemp_0.state as Number) + ") < " + _maxOutsideTemp)
}

This gives the non-intuitive result:

max temp (20 ℃) >= 23.0

Nothing I’ve tried has ever caused an exception. The comparison just returns unexpected results without complaint. Maybe there something wrong with the type’s Comparitor implementation of maybe I’m just missing the point. Either way, some guidance would be appreciated.

1 Like

Hi @alphie,

Did you see this blog post? The syntax is described in the “Scripts & Rules” section. I am not sure if you can store the QuantityType instances in a variable but your example comparison could be done in the following way:

if (Weather_MaxTemp_0.state < 23 [℃])
{
    logInfo("my.rules", ...)
}
1 Like

Hi Christoph,

Thanks for pointing that out. No, I had not found that page. Search terms including colons are difficult! After your clue, I know that the better search team would have been “Units of Measurement”. With that I was also able to find the corresponding article in the OpenHAB documentation.

I think there’s a good case for some mention of this feature being added to the rules article.

So, the final outstanding question is how to create variables with UOM in the rules DSL. I tried myVariable [℃], but that could not be parsed. I would rather avoid the nasty practice of “magic numbers”, especially when they’re being using in multiple places in the same file.

You are welcome. The second link to the rules article does not work. But I know what you are meaning. I created an issue for it.

how to create variables with UOM in the rules DSL

If I remember correctly it is possible. Please try to change your rule to something like this - please be aware that I wrote it down untested.

val _maxOutsideTemp = 23 [°C]
...
if (Weather_MaxTemp_0.state < _maxOutsideTemp)
{
    logInfo("my.rules", ...)
}

val represents a read-only value and the type is automatically inferred. var is a variable which can be changed during runtime. You only have to provide the type of a var if you do not assign an initial value to it (as it cannot be inferred in that case).

1 Like

That does work, thank you. I hadn’t noticed that implicit types were supported or the distinction between val and var, although I see that is actually covered in the rules article I linked above.

Sounds great. Now I have some work to do in my own environment. :wink:

FTR: Since OH 2.3 RC-2 we have to change the syntax for all examples.

if (Weather_MaxTemp_0.state > 23|"℃") {
    logInfo("my.rules", ...)
}
val _maxOutsideTemp = 23|"°C"
2 Likes

@cweitkamp How would you specify the unit in a rule condition? Using the syntax above in OH 2.3 official release can’t be parsed.

2018-05-31 15:29:55.158 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'thermostat.rules' has errors, therefore ignoring it: [35,48]: mismatched input '|' expecting 'then'
rule "Thermostat Temperature 68F"
when
  Item thermostatTemperature changed to 68|°F
then
  logInfo('thermostatTemperature ', 'Temperature: {}', thermostatTemperature.state)
end
2 Likes

Hi @jeshab,

To be honest I cannot answer that in a second. I have to do some testing. Keep you posted.

1 Like

Thanks. One aspect I forgot to mention is that the rule condition doesn’t trigger if just using the number without the unit of measure, which makes setting rule condition for specific values on these type of items impossible. The only workaround is to move the value condition into the rule.

Hi @jeshab,

after some research I would suggest to use the following syntax:

rule "Thermostat Temperature 68F"
when
    Item thermostatTemperature changed to "68 °F"
then
    logInfo("thermostatTemperature", "Temperature: {}", thermostatTemperature.state)
end
2 Likes

Just in case I need to link it in the future:

You can add the desired unit to the label of your item or in the sitemap. The framework will do the conversion automatically. e.g.

demo.items:

Number:Temperature testNumberTemperature

demo.sitemap:

sitemap demo label="My home automation" {
    Frame label="Temperature" {
        Text item=testNumberTemperature label="Temperature [%.1f %unit%]" icon="temperature"
        Text item=testNumberTemperature label="Temperature [%.1f °C]" icon="temperature"
        Text item=testNumberTemperature label="Temperature [%.1f °F]" icon="temperature"
    }
}

It will look like this:

Thanks @cweitkamp. I was able to confirm it is indeed working that way.

I have a NumberItem (using the mqtt binding), that I need to compare to a Number:Temperature Item (from the Nest binding), how can I do this?

Number myMqttNumber ...
Number:Temperature myNestAmbientTemperature ...
if (myMqttNumber.state == myNestAmbientTemperature.state) //Doesn't work at run time but no issues in VSCode

val a = myMqttNumber.state|°C
val b = (myMqttNumber.state as Number)|°C
val c = (myMqttNumber.state as Number).doubleValue|°C
// For all above, VSCode reports: no viable alternative at input '|'

As you can see above I have tried creating a new QuantityType using the state from the myMqttNumber item but I can’t get it to work.

I also thought about trying to extract the Number value from the QuantityType that is the state of the myNestAmbientTemperature item, but since it does not appear to be possible to cast the state to a QuantityType<Temperature> object, I can’t get VSCode to let me do that either.

val QuantityType<Temperature> foo = myNestAmbientTemperature.state as QuantityType<Temperature> 

VSCode reports Temperature cannot be resolved to a type.

I even tried:

import org.eclipse.smarthome.core.library.dimension.*
import javax.measure.quantity.*

but that didn’t help.

Any ideas?

You can do it in the other way::

var Number tempSalon = (TemperatureSalon.state as QuantityType<Number>).doubleValue

This is what I am using. I suppose that I get the value in the unit the item was last set.

1 Like

Thanks @Lolodomo, that works.
I have two additional questions:

  1. What is the best way to perform arithmetic on QuantityTypes?
val temp = myNestAmbientTemperature.state as QuantityType<Number>
temp += 2|°C

This causes the following error in VSCode: Type mismatch: cannot convert from QuantityType<?> to QuantityType<Number>

I am trying to increment the ambient temperature by two and then send this new value as a command to the setpoint item. Since the setpoint item is also a Number:Temperature I need a QuantityType.
I know I could extract the number value from the ambient temperature QuantityType, increment it and then create a new string item with the value and the ‘°C’ then send this as the command, but this seems messy. Plus I don’t understand why VSCode thinks 2|°C is a QuantityType<?> vs a QuantityType<Number>
To add to the confusion, this code produces the same error in VSCode:

val temp = myNestAmbientTemperature.state as QuantityType<Number>
temp += (2|°C as QuantityType<Number>)

:confused:

  1. Why QuantityType<Number> ?
    Is it possible to have a QuantityType that holds a value of any type other than Number? Why not just QuantityType or even QuantityType<Temperatue>?
    I am not questioning that you are correct, only trying to learn and understand.

Thanks!


Edit: More questions! :question: :question:

  1. Why does the following code underline num with error no viable alternative at input 'num'
val double num = 2
var QuantityType<Number> qt = num|°C

In other words, how do I create a QuantityType using a value from a variable?

  1. This blog post references QuantityType<Temperature> but as described earlier, this does not work in OH rules (as far as I can see) and so why the difference? @cweitkamp mentioned this blog post but due to this discrepancy I am actually more confused now. What am I missing?

…Sorry for the ridiculously long post. :blush:

2 Likes

Good questions but sorry I have not the answers.

@mcqwerty In some cases, I think it’s better to just test the rules with openHAB than just rely on the errors generated in VSCode. Especially with new features, I have seen some unnecessary errors in VSCode that are interpreted just fine in openHAB.

This works from my testing. I just want to point out that you can’t increment a variable defined as final (val). Also, I would recommend defining the QuantityType specific dimension over the overall Number one.

var temp = myNestAmbientTemperature.state as QuantityType<Temperature>
temp += 2|°C

As I mentioned above, QuantityType<Number> covers all dimensions. So QuantityType<Temperature> is derived from it.

Per my understanding, for QuantityType declaration using the value of a variable, you will need to use the declaration function. This works in my testing.

val double num = 2
val QuantityType<Temperature> qt = new QuantityType(num + "°C")

@jeshab Thanks for the reply:

Good call on the var vs val that was me carelessly editing code directly in the post.

Your code is not working for me - VSCode complains all over the place but even when I save I get an error in openhab.log: no viable alternative at input 'xC2' This looks like some kind of unicode issue since the xC2 in the log appears like a single character. I’m not sure how to explain but my guess is that the °C is the problem. I tried with copy pasting directly from .toString of real temperature states but all I get is either xE2 or xC2 in the log with the above error.
Also, with your sample code, do you have any errors in VSStudio? If not, did you have to import anything to get the namespace for the Temperature Class to stop VSCode complaining?

I tried:

import org.eclipse.smarthome.core.library.dimension.*
import javax.measure.quantity.*

but Temperature still causes Temperature cannot be resolved to a type in VSCode.

I think these are two different problems and I can live with the VSCode complaints but the unicode issue is stopping the compile from working.


OK that makes sense, thanks.


Thanks, that works even though VSCode complains about Temperature, it still compiles and works fine.

Looking at the source code here, I see:

public QuantityType(Number value, Unit<T> unit) {
        // Avoid scientific notation for double
        BigDecimal bd = new BigDecimal(value.toString());
        quantity = (Quantity<T>) Quantities.getQuantity(bd, unit);
    }

To me that implies that we could create a QuantityType without resorting to magic strings but I can’t work out how to get it to work. After re-reading the blog post referenced by @cweitkamp I tried this:

val double num = 2
val QuantityType<Temperature> qt = new QuantityType(num, SIUnits.CELSIUS)

VSCode complains a lot but it does seem to compile. However at run time I get the following error in the log: The name 'SIUnits' cannot be resolved to an item or type;
Any ideas what the syntax should be?

You seem to have pinpointed the issue. You should check your rule file encoding and convert it to UTF-8 if necessary. I unfortunately don’t use VSCode that much so I can’t comment on the errors you are seeing.

I am not sure about this. I just tried to give a practical solution to your problem. If you want to use that specific method, you will probably need to import the library that defines SIUnits.