Absolute value of a decimal

thanks a lot, opus, for the fast reply.

So, then I was mistaken (at least Eclipse Smart Home Designer told me that it only works for int). I will try it out…

Best regards
Rolf

There is a version of the abs method that works with all the numeric primitives. But note that last word, “primitives”. You will probably have to call

Math::abs((AF.state as Number).floatValue)

NOTE: In my experience it is better to cast to Number rather than DecimalType. I think this changed recently, but it used to be the case that to call a static method on a class you had to use :: instead of .

Finally, if you are working with OH 2, you should be using VSCode with the openHAB extension. ESHD is pretty much dead. Development has stopped long since and it has been behind almost since the release of OH 2.0.

Dear all

Thanks a lot for your comments so far. However I still get it not working. In the log file get the follwoeing error message:

Rule ‘Update Aussentemperatur’: Could not cast NULL to java.lang.Number; line 48, column 50, length 20

My rule looks as follows:

rule “Update Aussentemperatur”
when
Item AF_c received update
then
if ((Lock_i.state == ON) && (Lock_m.state == ON) && (Lock_a.state == ON)){
if (Math::abs((AF_c.state as Number).floatValue - (AF_m.state as Number).floatValue) >
Math::abs((AF_c.state as Number).floatValue - (AF_a.state as Number).floatValue))
AF_a.postUpdate(AF_c.state as DecimalType)
else
AF_m.postUpdate(AF_c.state as DecimalType)
}
end

Could anyone please help me. It seems, I’m still in the beginner liga

Many thanks and best regards
Rolf

The Item that is being used as a Number on line 48 doesn’t have a state. NULL indicates an Item that is uninitialized. You need to add some tests up front in your rule to skip the Rule unless all of your Items that you use in that rule have a value.

if(AF_m.state == NULL || AF_c.state == NULL || AF_a.state == NULL) return;
1 Like

Thank you very much for the fast replay. But how can i initially assign a value to AF_m and AF_a?
(I defined AF_m and AF_a as number in the items file). In the rule i want to assign them the value of AF_c based in the defined conditions.

Best regards
Rolf

It is unclear what these Items are or where they come from. Do these Items have bindings or are they linked to Channels? If so then how do those bindings or channels work? That will give you the answer.

If these are Design Pattern: Unbound Item (aka Virtual Item) then you can initialize them when OH first comes up similar to Design Pattern: Encoding and Accessing Values in Rules (approach 2) or you can just check in your Rule and give it an initial value when the Rule triggers and the Item is NULL.

NOTE: Unrelated but in the future please How to use code fences

rule “Update Aussentemperatur”
when
    Item AF_c received update
then
    val c = if(AF_c.state == NULL) 0 else Math::abs((AF_c.state as Number).floatValue)
    val m = if(AF_m.state == NULL) 0 else Math::abs((AF_m.state as Number).floatValue)
    val a = if(AF_a.state == NULL) 0 else Math::abs((AF_m.state as Number).floatValue)

    if ((Lock_i.state == ON) && (Lock_m.state == ON) && (Lock_a.state == ON)){
        if(c - m > c - a) AF_a.postUpdate(c)
        else              AF_m.postUpdate(c)
    }
end

Note that your Item names are basically meaningless out of context so I have no idea if it makes sense to initialize the values in this way.

Dear Rich

Thank you very much for your reply. Originally I didn‘t want to bother the community with too much unnecessary content but I realize that it is necessary to give more context on what I want to achieve. Sorry for that. Context is as follows:

Situation: In an ebus binding, I get (among others) two signals, i.e. integer value of a temperature (item AF_i) and a signal (item AF_c) which alternatively sends telegrams with exact values of temperature (i.e. with digits after the comma) and mean value of temperature over the last 24 hours.

Goal: split AF_c in the virtual items AF_a (exact actual temperature) and an item AF_m (mean value of temperature of last 24 hours)

Proposed approach: At Systemstart wait till ebus sends a firsts AF_i (set Lock_i go ON) with rule “Integer”. Afterwards, as soon as Integer value of AF_c is unequal to AF_i (because i did not know how to extract integer value of a float number, i have worked with inequality tests), assign value of AF_c to AF_m (Lock_m set to ON) with rule “Initiales Setzen mittlere Aussentemperatur", then wait for next telegram where Integer value of AF_c is equal to AF_i and assign value AF_c to AF_a (Lock_a set to ON) (Assumption: temperature values have not changed between the two telegrams of the ebus) with rule “Initiales Setzen Aussentemperatur”. Thereafter compare new AF_c with existing AF_m and AF_a and assign new AF_c to either AF_m or AF_m based on which of the two old values is nearer to the new AF_c with the rule “Update Aussentemperatur”.

The code I propose to implement that looks as follows (i hope that I have the fence correctly implemented). Many thanks in advance for your great support.

rule  "Systemstart"
when
  System started
then
  Lock_i.postUpdate(OFF)
  Lock_m.postUpdate(OFF)
  Lock_a.postUpdate(OFF)
end

rule "Integer"
when
Item AF_i received update 
then
  Lock_i.postUpdate(ON)
end

rule "Initiales Setzen mittlere Aussentemperatur"
when 
 Item AF_c received update 
then
 if ((Lock_i.state == ON) && (Lock_m.state == OFF)){
    if (((AF_c.state as DecimalType) - (AF_i.state as DecimalType) < 0) || ((AF_c.state as DecimalType) - (AF_i.state as DecimalType) > 1))
       AF_m.postUpdate(AF_c.state)
       Lock_m.postUpdate(ON)   
 }
end

rule "Initiales Setzen Aussentemperatur"
when 
 Item AF_c received update 
then
 if ((Lock_i.state == ON) && (Lock_m.state == ON) && (Lock_a.state == OFF) && 
 (((AF_m.state as DecimalType) - (AF_i.state as DecimalType) < 0) || ((AF_m.state as DecimalType) - (AF_i.state as DecimalType) > 1))) {
  if (((AF_c.state as DecimalType) - (AF_i.state as DecimalType) > 0) && ((AF_c.state as DecimalType) - (AF_i.state as DecimalType) < 1))
     AF_a.postUpdate(AF_c.state as DecimalType)
     Lock_a.postUpdate(ON)   
}
end 

rule "Update Aussentemperatur"
when 
 Item AF_c received update 
then
 if ((Lock_i.state == ON) && (Lock_m.state == ON) && (Lock_a.state == ON)){
    if (Math::abs((AF_c.state as Number).floatValue - (AF_m.state as Number).floatValue) > 
    Math::abs((AF_c.state as Number).floatValue - (AF_a.state as Number).floatValue)) 
       AF_a.postUpdate(AF_c.state as DecimalType) 
    else
      AF_m.postUpdate(AF_c.state as DecimalType)
}
end

Dear Rich

I think my last post was just the description of what I want to achieve. The problem that the code does not work still exists, i.e. NULL values in the rule “Update Aussentemperatur”, see post 5.

Since when reaching rule “Update Aussentemperatur” the AF_m and AF_a should definitely have assigned values, I suspect that the problem of the code is rather with rule “Initiales Setzen Aussentemperatur” and “Initiales Setzen mittlere Aussentemperatur”, i.e. command AF_m.postUpdate(AF_c.state) and AF_a.postUpdate(AF_c.state as DecimalType) do not work.

could you please give me a hint of what is wrong here? I’m lost

Many thanks and best regards
Rolf

I still think that the Items are coming up NULL because they rule is running behind you get both events.

You need to do like I demonstrated in my rule. You need to check for NULL and either use a default value or skip that execution of the rule. The next time it executes all your items should have received a value and it should work.

Dear Rich

Thank you very much for your reply. What I still do not understand is that in the initial run within rule “Initiales Setzen mittlere Aussentemperatur” no value for AF_m is set. What I did for test pruposes is to reduce the rule set to:

rule "Systemstart"
when
    System started
then
    Lock_i.postUpdate(OFF)
    Lock_m.postUpdate(OFF)
   // Lock_a.postUpdate(OFF)
end

rule "Integer"
when
	Item AF_i received update 
then
    Lock_i.postUpdate(ON)
end

rule "Initiales Setzen mittlere Aussentemperatur"
when 
	Item AF_c received update 
then
	if (Lock_i.state == ON){
	AF_m.postUpdate(AF_c.state as DecimalType)
		Lock_m.postUpdate(ON)  	
	}
end 

with items in item file:

Switch Lock_i
Switch Lock_m
Number AF_m

and with sitemap:

Text	  	item=AF_m		

However no AF_m

– sorry I mistakenly pushed to send button, so neglect the last post –

Dear Rich

Thank you very much for your reply. What I still do not understand is that in the initial run within rule “Initiales Setzen mittlere Aussentemperatur” no value for AF_m is set. What I did for test pruposes is to reduce the rule file to:

rule "Systemstart"
when
    System started
then
    Lock_i.postUpdate(OFF)
    Lock_m.postUpdate(OFF)
end

rule "Integer"
when
	Item AF_i received update 
then
    Lock_i.postUpdate(ON)
end

rule "Initiales Setzen mittlere Aussentemperatur"
when 
	Item AF_c received update 
then
	if (Lock_i.state == ON){
	        AF_m.postUpdate(AF_c.state as DecimalType)
		Lock_m.postUpdate(ON)  	
	}
end 

with items in item file:

Switch Lock_i
Switch Lock_m
Number AF_m

and with sitemap:

Text	  	item=AF_m	

However no AF_m (which should be basically equal to AF_c) is displayed in my sitemap, i.e. the command
AF_m.postUpdate(AF_c.state as DecimalType)
seems not to be executed. The command Lock_m.postUpdate(ON) is executed.

What needs to be changed that I get a AF_m value displayed?

Many thanks for your help!

Best regards
Rolf

Look in events.log and add logging to your rule so you can see the order things are processing. I bet you will see that Initiales Setzen mittlere Aussentemperatur runs before Integer and therefore never calls the postUpdate.

You have no control over the order that the events come in so your roles need to handle them coming in in any order.

Stupid question: how do i switch rule debugger on?
Log:set DEBUG …?

Thank you for a hint

Rule logging is already on. https://docs.openhab.org/administration/logging.html#create-log-entries-in-rules

The log statements will appear in openhab.log or the karaf console.

Dear Rich

I’m on the way to rsolve the issue, will report back as soon as it is done. One remaining question: How do I extract the Integer Value of a number? At the moment it is solved in a cumbersome way, i.e. test if IntValue of AF_c is equal to AF_i (this is an Integer Value) :

if
((AF_c.state as DecimalType) - (AF_i.state as DecimalType) > 0) && ((AF_c.state as DecimalType) - (AF_i.state as DecimalType) < 1)
.....

I suspect it would be:

if ((AF_c.state as DecimalType).intValue == (AF_i.state as DecimalType).intValue) 
....

Is that correct?

Many thanks for a hint to that

Best regards
Rolf

One more question: I try to test that hypothesis via a logging:
logInfo("Aussentemp.rules", "AF_c Int: %.1f", (AF_c.state as DecimalType).intValue)

In the log I only get:
09:19:18.019 [INFO ] [arthome.model.script.Aussentemp.rules] - AF_c Int: %.1f

instead of the value… What could be wrong here?

Yes, though there is nothing work with your first approach.

I don’t write log statements like that but I’m pretty sure what your have isn’t right. There had to be an additional number to indicate which argument goes where.

https://docs.openhab.org/administration/logging.html#create-log-entries-in-rules

Also, use %d when logging integers.

Dear Rich

Still struggling with the code. One of my items is a string (Heat_Request_2), which has “integer” values). I want to convert it into a Integer val, I tried the following:
val c = Integer::parseInt(Heat_Request_2.state)

But this does not work. What could be the mistake?

Again, I’m very thankful for your help.

Best regards
Rolf

It really helps if you post the error in the log or from VSCode when something doesn’t work.

val c = Integer::parseInt(Heat_Request_2.state.toString)

That might fix it but since I don’t know what the error is…

Perfect, that works! Thank you a lot!!