How to concatenate values into one to display a summary

As one task you get familiar with rules I want to create a summary of several values for each heating in our house. Actually I have the following values for each heating, which takes far too much space

I want to summarize that to one line:

Zusammenfassung: 20,6°C / 23,0°C / 2,7V / 99% / Boost:0 / Batterie: Gut / Fehler: Nein

which then is saved in a different variable (Office_heat_left) and shown on screen.

With some samples in mind I started with the following rule:

rule "HeatingDescription"
 when
      Item Office_Heat_Left_ActTemp received update or
      Item Office_Heat_Left_TargetTemp received update or
      Item Office_Heat_Left_Fault received update or
      Item Office_Heat_Left_StateBat received update or
      Item Office_Heat_Left_Valve received update or
      Item Office_Heat_Left_Boost received update or
      Item Office_Heat_Left_LowBat received update or 
      System started
 then
	val Heat_Left_ActTemp = Office_Heat_Left_ActTemp.state
	val Heat_Left_TargetTemp = Office_Heat_Left_TargetTemp.state
	Office_Heat_Left.postUpdate(Heat_Left_ActTemp.format("%.0f"))
 end

The above at least saves the value of Heat_Left_ActTemp into the screen variable but when I change format("%.0f") to format("%.1f") to have the tenth digit it fails with a

[WARN ] [.c.i.events.EventPublisherImpl] - given new state is NULL, couldn’t post update for 'Office_Heat_Left’

Same when I try to add a “°C” using i.e. Office_Heat_Left.postUpdate(Heat_Left_ActTemp.format("%.0f")) or Office_Heat_Left.postUpdate(Heat_Left_ActTemp.format("%.0f") + “°C”).

What is a good source to get familiar with this Xtend language? Also the question what modules to import is completele open to me.

Can someone help with some hints and/or pointers?

Br Matthias

Do you have Office_Heat_Left defined as a String item, shown in the sitemap with the label ending with "[%s]"?

Here are issues with your approach:

  1. Some items you want to format may not have values yet, so you have to make sure you don’t try to format non-numeric values as numbers.
  2. You probably want to use the static format method of the Java String class. See the JavaDoc on String::format
  3. You may want to trigger on changed instead of received update, since you are only interested in when values change.

Would the following work?

rule "HeatingDescription"
when
    Item Office_Heat_Left_ActTemp changed or
    Item Office_Heat_Left_TargetTemp changed or
    Item Office_Heat_Left_Fault changed or
    Item Office_Heat_Left_StateBat changed or
    Item Office_Heat_Left_Valve changed or
    Item Office_Heat_Left_Boost changed or
    Item Office_Heat_Left_LowBat changed or 
    System started
then
    val Number Heat_Left_ActTemp = if (Office_Heat_Left_ActTemp.state instanceof DecimalType) Office_Heat_Left_ActTemp.state else 0.0
    val Number Heat_Left_TargetTemp = if (Office_Heat_Left_TargetTemp.state instanceof DecimalType) Office_Heat_Left_TargetTemp.state else 0.0
    var String formattedString = String::format("%1$.0f°C / %2$.0f°C", Heat_Left_ActTemp, Heat_Left_TargetTemp)
    Office_Heat_Left.postUpdate(formattedString)
end

Hi watou,

thanx for your quick reply. I understand what you write and also your code. I just do not know how to write it myself…

I took over your code, which results in the following error messages:

Error during the execution of rule ‘HeatingDescription’: f != org.openhab.core.library.types.DecimalType
Error during the execution of rule ‘HeatingDescription’: f != org.openhab.core.library.types.DecimalType
Error during the execution of rule ‘HeatingDescription’: f != org.openhab.core.library.types.DecimalType

I guess that is related to my “What to import question”. How do I find which import has to be imported?

I added "import org.openhab.core.library.types.DecimalType"
and error messages reduce from three to one. But why does this one remains, as it is identical to the two that disappeared?

Error during the execution of rule ‘HeatingDescription’: f != org.openhab.core.library.types.DecimalType

Sorry I forgot the import statement!

I may still have that wrong. Let me try again:

import java.Math.BigDecimal
import org.openhab.core.library.types.DecimalType

rule "HeatingDescription"
when
    Item Office_Heat_Left_ActTemp changed or
    Item Office_Heat_Left_TargetTemp changed or
    Item Office_Heat_Left_Fault changed or
    Item Office_Heat_Left_StateBat changed or
    Item Office_Heat_Left_Valve changed or
    Item Office_Heat_Left_Boost changed or
    Item Office_Heat_Left_LowBat changed or 
    System started
then
    val BigDecimal Heat_Left_ActTemp = if (Office_Heat_Left_ActTemp.state instanceof DecimalType) (Office_Heat_Left_ActTemp.state as DecimalType).toBigDecimal else new BigDecimal::ZERO
    val BigDecimal Heat_Left_TargetTemp = if (Office_Heat_Left_TargetTemp.state instanceof DecimalType) (Office_Heat_Left_TargetTemp.state as DecimalType).toBigDecimal else BigDecimal::ZERO
    var String formattedString = String::format("%1$.0f°C / %2$.0f°C", Heat_Left_ActTemp, Heat_Left_TargetTemp)
    Office_Heat_Left.postUpdate(formattedString)
end

Pretty verbose, huh? I am going to make sure that one of my scripting options in OH 2 is good old Lua 5.1, and fiddling with types will be gone, gone, gone!

After changing the code to the BigDecimal version, the error message disappears, but the screen variable is not updated. Guess it is time for C64 debugging (using logging output).

Thanx for your help.

One last tweak:

import java.Math.BigDecimal
import org.openhab.core.library.types.*

rule "HeatingDescription"
when
    Item Office_Heat_Left_ActTemp changed or
    Item Office_Heat_Left_TargetTemp changed or
    Item Office_Heat_Left_Fault changed or
    Item Office_Heat_Left_StateBat changed or
    Item Office_Heat_Left_Valve changed or
    Item Office_Heat_Left_Boost changed or
    Item Office_Heat_Left_LowBat changed or 
    System started
then
    val BigDecimal Heat_Left_ActTemp = if (Office_Heat_Left_ActTemp.state instanceof DecimalType) (Office_Heat_Left_ActTemp.state as DecimalType).toBigDecimal else new BigDecimal::ZERO
    val BigDecimal Heat_Left_TargetTemp = if (Office_Heat_Left_TargetTemp.state instanceof DecimalType) (Office_Heat_Left_TargetTemp.state as DecimalType).toBigDecimal else BigDecimal::ZERO
    var String formattedString = String::format("%1$.0f°C / %2$.0f°C", Heat_Left_ActTemp, Heat_Left_TargetTemp)
    Office_Heat_Left.postUpdate(new StringType(formattedString))
end

Thanx again. The reason for the missing output was something else. You changed the condition to “changed” instead of “updated” and the values just did not change.

I returned to “updated” and get error messages now again:

Office_Heat_Left_ActTemp state updated to 22.30
Office_Heat_Left_TargetTemp state updated to 23.00
Error during the execution of rule 'HeatingDescription': The name 'ZERO' cannot be resolved to an item or type.
Error during the execution of rule 'HeatingDescription': The name 'ZERO' cannot be resolved to an item or type.
Error during the execution of rule 'HeatingDescription': The name 'ZERO' cannot be resolved to an item or type.

Aw geez. I don’t know why that is. But if you change BigDecimal::ZERO to new BigDecimal(0) maybe we can hopefully get past that?

At least form me, it gets more complicated:

import java.Math.BigDecimal
import org.openhab.core.library.types.*

rule "HeatingDescription"
when
	Item Office_Heat_Left_ActTemp received update or
	Item Office_Heat_Left_TargetTemp received update or
	Item Office_Heat_Left_Fault received update or
	Item Office_Heat_Left_StateBat received update or
	Item Office_Heat_Left_Valve received update or
	Item Office_Heat_Left_Boost received update or
	Item Office_Heat_Left_LowBat received update or 
	System started
then
	val BigDecimal Heat_Left_ActTemp = if (Office_Heat_Left_ActTemp.state instanceof DecimalType) (Office_Heat_Left_ActTemp.state as DecimalType).toBigDecimal else new BigDecimal(0)
	val BigDecimal Heat_Left_TargetTemp = if (Office_Heat_Left_TargetTemp.state instanceof DecimalType) (Office_Heat_Left_TargetTemp.state as DecimalType).toBigDecimal else BigDecimal(0)
	var String formattedString = String::format("%1$.0f°C / %2$.0f°C", Heat_Left_ActTemp, Heat_Left_TargetTemp)
	logInfo(">>> HeatingDescription <<<", formattedString)
	Office_Heat_Left.postUpdate(new StringType(formattedString))
end

leads to:

2016-01-23 14:57:30.947 [ERROR] [m.r.internal.engine.RuleEngine] - Error during the execution of startup rule 'HeatingDescription': Could not invoke method: java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]) on instance: null

Why is the rule language matching the wrong format method signature? Makes no sense to me. Frustrating.

Maybe if we feed it the stupid Locale object it wants so desperately? (Also changed Math to math in BigDecimal import)

import java.math.BigDecimal
import java.util.Locale
import org.openhab.core.library.types.*

rule "HeatingDescription"
when
	Item Office_Heat_Left_ActTemp received update or
	Item Office_Heat_Left_TargetTemp received update or
	Item Office_Heat_Left_Fault received update or
	Item Office_Heat_Left_StateBat received update or
	Item Office_Heat_Left_Valve received update or
	Item Office_Heat_Left_Boost received update or
	Item Office_Heat_Left_LowBat received update or 
	System started
then
	val BigDecimal Heat_Left_ActTemp = if (Office_Heat_Left_ActTemp.state instanceof DecimalType) (Office_Heat_Left_ActTemp.state as DecimalType).toBigDecimal else new BigDecimal(0)
	val BigDecimal Heat_Left_TargetTemp = if (Office_Heat_Left_TargetTemp.state instanceof DecimalType) (Office_Heat_Left_TargetTemp.state as DecimalType).toBigDecimal else BigDecimal(0)
	var String formattedString = String::format(Locale::default, "%1$.0f°C / %2$.0f°C", Heat_Left_ActTemp, Heat_Left_TargetTemp)
	logInfo(">>> HeatingDescription <<<", formattedString)
	Office_Heat_Left.postUpdate(new StringType(formattedString))
end

The error messages are gone, and the variable is updated, just misinterpretes the ° character.

[c.internal.ModelRepositoryImpl] - Refreshing model 'default.rules'
[m.s.>>> HeatingDescription <<<] - 22?C / 23?C
[runtime.busevents             ] - Office_Heat_Left state updated to 22?C / 23?C

So far it seems to be trial and error to write a rule using xtend. ow does one learn that the correct way? The “documentation” on xtend website doesn’t really help me, as it explains the basic structures (which are mostly identical to any other language) and shows the class hirarchy. But how to find the right classes etc?

Is there an overview/list/tree of the openhab package somewhere?

Matthias

You are not alone in being frustrated by holes in the documentation. Generally speaking, it has been a “learn by example” process. Several people are much happier with the JSR223 support, where you can write rules in Jython or JavaScript. I think openHAB 2 will reach a point where we have well documented languages and context, and verbosity and guessing like this thread won’t be required. There is much that is good about the Xtend-like language, but not having a concrete, definitive language reference and guide has been painful a lot of the time.

I came from the clunky Vera home automation world, but one thing they did right (by accident, I suspect) was to use Lua as a scripting language: tight, simple, fast, typeless. I hope to introduce Lua 5.1 to openHAB 2 at some point, unless someone beats me to it.

So looking forward to OH2 (I guess the documentation you expect will not be there before final release).

Any chance to get the ° in the output instead of the “?”. I would guess it has something to do with the locales?

I was assuming that the “?” was only in the events.log output, but that it would look correct in the UI? Is it wrong in the UI as well?

Yes it is.

Chrome is set to

Once again, I don’t know, but I see this comment about making the degree symbol show up correctly by replacing it with &#176, but under slightly different circumstances:

That really helped! Even that I found the reason, which was something completely different:

I copied your code here and copied it into the rules file. When I now changed it into &#176 I saw that the “f” (float) was very little and got larger when I entered the code. So that indicates that one shouldn’t copy code straight over, but to care about formatting.

Anyway, saying thank you the again.

Matthias

1 Like