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
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.
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:
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.
You probably want to use the static format method of the Java String class. See the JavaDoc on String::format
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
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
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).
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.
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?
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.
Once again, I don’t know, but I see this comment about making the degree symbol show up correctly by replacing it with °, 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 ° 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.