I have an input that tells OpenHAB if my generator is running and I display this in my site map, but I also would like to keep track of total generator runtime in hours. Anyone know a way to do that inside OpenHAB?
Do you want total hours for all time, the last week, the last day, last hour?
Assuming you want for all time I would approach the problem as follows:
First make sure you have persistence setup or you will lose your hours on a restart of openHAB.
Then Iād create a Number item to store the hours and a rule to keep adding to that number when the generator is on.
Dealing with time is tricky and Iāve almost certainly gotten something minor wrong below but hopefully it is close enough to get you started. Iām also not 100% certain on the syntax. I donāt have Designer in front of me right now.
Items:
Switch Generator ...
Number GeneratorRuntimeHours ... // used on sitemap
number GeneratorRuntimeMSec ... // used to store intermediate values which get rounded
Rules:
var generatorRunning = false
var whenStarted = null
rule "Generator started"
when
Item Generator received command ON
then
generatorRunning = true
whenStarted = now
end
rule "Generator stopped"
when
Item Generator received command OFF
then
generatorRunning = false
val timeSinceLastupdate = now.millis - whenStarted.millis
whenStarted = null
val totalMsec = (GeneratorRuntimeMsec.state as Number) + timeSinceLastupdate
GeneratorRuntimeMsec.postUpdate(totalMsec)
GeneratorRunteimHours.postUpdate(java.lang.Math.round(totalMsec/1000/60/60)) // rounds msec to hours
end
rule "Increment Generator Runtimes"
when
Time cron "* */1 * * * ?" // update every minute
then
if(generatorRunning) {
val timeSinceLastupdate = now.millis - whenStarte.millis // get how long it has been since the last update
whenStarted = now // update whenStarted as soon as possible
val totalMsec = (GeneratorRuntimeMsec.state as Number) + timeSinceLastupdate // calculate total runtime
GeneratorRuntimeMsec.postUpdate(totalMsec) // post the full runtime
GeneratorRuntimeHours.postUpdate(java.lang.Math.round(totalMsec/1000/60/60)) // rounds msec to hours
}
end
You could probably do this with a timer as well but Iām more confident this will work. Iām also pretty sure a few milliseconds will be dropped here and there but since you mainly care about hours I donāt think they will amount to anything significant. Also, the calculation of the total runtimes should probably be put into a lambda rather than repeating code.
Had to define what the variables were such as:
var boolean generatorRunning = false
var dateTime whenStarted = null
Now I get the following error I am not sure how to pass.
2015-10-20 11:16:02.005 [ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Increment Generator Runtimes
java.lang.RuntimeException: The name '<XFeatureCallImplCustom>.millis' cannot be resolved to an item or type.
P.S. Thanks so much for the help!
Hmmm.
Lets be explicit about what whenStarted is:
var org.joda.time.DateTime whenStarted = null
The error is telling me that it is tyring to use the wrong type, one that doesnāt have a .millis method.
That worked! Now I get:
2015-10-20 12:06:08.522 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Generator stopped': cannot invoke method public long org.joda.time.base.BaseDateTime.getMillis() on null
Also seeing:
2015-10-20 12:09:15.852 [INFO ] [runtime.busevents ] - Generator received command ON
2015-10-20 12:09:16.026 [ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Increment Generator Runtimes
org.eclipse.emf.common.util.WrappedException: org.eclipse.xtext.util.PolymorphicDispatcher$NoSuchMethodException: Couldn't find method ''_assignValue'' for objects [JvmVoid: (eProxyURI: generator.rules#xtextLink_::0.2.2.2.0.0.1.0.0::0::/1), <null> timeSinceLastupdate <XBinaryOperationImplCustom>, 148, org.eclipse.xtext.xbase.interpreter.impl.DefaultEvaluationContext@7567990e, org.eclipse.xtext.util.CancelIndicator$1@5a8fd14d]
Hmmm. That second error might be causing the first one you posted.
We might need to do more explicit casting when we calculate the time. Lets break the rule up a bit to see if we can figure out what line is having the problems.
Lets try:
rule "Increment Generator Runtimes"
when
Time cron "* */1 * * * ?" // update every minute
then
if(generatorRunning) {
logInfo("GenUptime", "Getting now")
val long nowMsec = now.millis
logInfo("GenUptime", "Getting whenStarted")
val long wsMsec = whenStarted.millis
logInfo("GenUptime", "Resetting whenStarted")
whenStarted = now
logInfo("GenUptime", "Calculating time difference")
val long timeSinceLastUpdate = nowMsec - wsMsec
logInfo("GenUptime", "Getting total Uptime in msec")
val long oldVal = GeneratorRuntimeMsec.state
logInfo("GenUptime", "Adding current time to total")
val long totalMsec = oldVal + timeSinceLastupdate // calculate total runtime
logInfo("GenUptime", "Posting totalMsec to Item")
GeneratorRuntimeMsec.postUpdate(totalMsec) // post the full runtime
logInfo("GenUptime", "Calculating hours")
val long hours = java.lang.Math.round(totalMsec/1000/60/60)
logInfo("GenUptime", "Posting hours")
GeneratorRuntimeHours.postUpdate(hours)
}
end
This way we will see exactly whiat line it dies on. This also might clear the error if the problem is the engine canāt automatically cast things the way we need them to.
In the āGenerator Stoppedā rule, you are referencing whenStarted
, but this assumes that the āGenerator Startedā or āGenerator Runtimesā rules had been run previously, which is not guaranteed.
<smacks head>
Duh.
So the rule should be:
rule "Generator stopped"
when
Item Generator received command OFF
then
if(generatorRunning){
generatorRunning = false
val timeSinceLastupdate = now.millis - whenStarted.millis
whenStarted = null
val totalMsec = (GeneratorRuntimeMsec.state as Number) + timeSinceLastupdate
GeneratorRuntimeMsec.postUpdate(totalMsec)
GeneratorRunteimHours.postUpdate(java.lang.Math.round(totalMsec/1000/60/60)) // rounds msec to hours
}
end
Its hard to code when you canāt actually run it.
The other error though is still in play.
Hmm, not seeing this run at all. Its like cron is not kicking off at all.
The cron rule will only do something if the Generator started rule is executed.
Maybe it would be better to just use the state of the Generator switch instead of the boolean at the top of the rule.
var org.joda.time.DateTime whenStarted = null
rule "Generator started"
when
Item Generator received command ON
then
whenStarted = now
end
rule "Generator stopped"
when
Item Generator received command OFF
then
if(whenStarted != null) {
// do the same as the if(Generator.state == ON && whenStarted != null) in the increment rule
}
whenStarted = null
end
rule "Increment Generator Runtimes"
when
Time cron "* */1 * * * ?"
then
if(Generator.state == ON && whenStarted != null){
logInfo("GenUptime", "Getting now")
val long nowMsec = now.millis
logInfo("GenUptime", "Getting whenStarted")
val long wsMsec = whenStarted.millis
logInfo("GenUptime", "Resetting whenStarted")
whenStarted = now
logInfo("GenUptime", "Calculating time difference")
val long timeSinceLastUpdate = nowMsec - wsMsec
logInfo("GenUptime", "Getting total Uptime in msec")
val long oldVal = GeneratorRuntimeMsec.state
logInfo("GenUptime", "Adding current time to total")
val long totalMsec = oldVal + timeSinceLastupdate // calculate total runtime
logInfo("GenUptime", "Posting totalMsec to Item")
GeneratorRuntimeMsec.postUpdate(totalMsec) // post the full runtime
logInfo("GenUptime", "Calculating hours")
val long hours = java.lang.Math.round(totalMsec/1000/60/60)
logInfo("GenUptime", "Posting hours")
GeneratorRuntimeHours.postUpdate(hours)
}
else if(Generator.state == ON && whenStarted == null){
logInfo("GenUptime", "Generator is ON but Generator started rule never executed")
whenStarted = now
}
else {
logInfo("GenUptime", "Generator is not running")
}
end
Iāve tons of logging in there. They can be removed once we get this working.
2015-10-20 13:12:31.001 [INFO ] [openhab.model.script.GenUptime] - Generator is not running
2015-10-20 13:12:31.403 [INFO ] [runtime.busevents ] - Generator received command ON
2015-10-20 13:12:32.008 [INFO ] [openhab.model.script.GenUptime] - Getting now
2015-10-20 13:12:32.021 [INFO ] [openhab.model.script.GenUptime] - Getting whenStarted
2015-10-20 13:12:32.034 [INFO ] [openhab.model.script.GenUptime] - Resetting whenStarted
2015-10-20 13:12:32.052 [INFO ] [openhab.model.script.GenUptime] - Calculating time difference
2015-10-20 13:12:32.072 [INFO ] [openhab.model.script.GenUptime] - Getting total Uptime in msec
2015-10-20 13:12:32.116 [ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Increment Generator Runtimes
java.lang.RuntimeException: The name 'GeneratorRuntimeMsec' cannot be resolved to an item or type.
Looks like I had a typo in my Items way up above in my original posting. It should be āNumber GeneratorRuntimeMsecā (upper case āNā on number).
Yep, I have that:
Switch Generator "Generator" <generator>
Number GeneratorRuntimeHours
Number GeneratorRuntimeMSec
Another typo. In the rule I have GeneratorRuntimeMsec
but the Item is GeneratorRuntimeMSec
. Note the upper case āSā for MSec.
2015-10-20 13:29:15.001 [INFO ] [openhab.model.script.GenUptime] - Getting now
2015-10-20 13:29:15.001 [INFO ] [openhab.model.script.GenUptime] - Getting whenStarted
2015-10-20 13:29:15.001 [INFO ] [openhab.model.script.GenUptime] - Resetting whenStarted
2015-10-20 13:29:15.001 [INFO ] [openhab.model.script.GenUptime] - Calculating time difference
2015-10-20 13:29:15.001 [INFO ] [openhab.model.script.GenUptime] - Getting total Uptime in msec
2015-10-20 13:29:15.001 [INFO ] [openhab.model.script.GenUptime] - Adding current time to total
2015-10-20 13:29:15.021 [ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Increment Generator Runtimes
java.lang.RuntimeException: The name 'timeSinceLastupdate' cannot be resolved to an item or type.
Another typo. I wish I had Designer with me at the moment. Grrrrr.
It should be
val long totalMsec = oldVal + timeSinceLastUpdate
It doesnāt help that this keyboard has a gimpy shift key that doesnāt always work.
2015-10-20 13:40:40.001 [INFO ] [openhab.model.script.GenUptime] - Getting now
2015-10-20 13:40:40.001 [INFO ] [openhab.model.script.GenUptime] - Getting whenStarted
2015-10-20 13:40:40.001 [INFO ] [openhab.model.script.GenUptime] - Resetting whenStarted
2015-10-20 13:40:40.001 [INFO ] [openhab.model.script.GenUptime] - Calculating time difference
2015-10-20 13:40:40.002 [INFO ] [openhab.model.script.GenUptime] - Getting total Uptime in msec
2015-10-20 13:40:40.002 [INFO ] [openhab.model.script.GenUptime] - Adding current time to total
2015-10-20 13:40:40.003 [ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Increment Generator Runtimes
java.lang.IllegalStateException: Could not invoke method: org.eclipse.xtext.xbase.lib.LongExtensions.operator_plus(long,long) on instance: null
Oh, I think I know the problem. Because the rule has never run before the GeneratorRuntimeMSec is Undefined. Lets check for that and use 0 if it is undefined.
val long oldVal = if (GeneratorRuntimeMSec.state== Undefined) 0 else GeneratorRuntimeMSec.state
Same:
2015-10-20 14:41:47.007 [INFO ] [openhab.model.script.GenUptime] - Getting now
2015-10-20 14:41:47.023 [INFO ] [openhab.model.script.GenUptime] - Getting whenStarted
2015-10-20 14:41:47.036 [INFO ] [openhab.model.script.GenUptime] - Resetting whenStarted
2015-10-20 14:41:47.054 [INFO ] [openhab.model.script.GenUptime] - Calculating time difference
2015-10-20 14:41:47.075 [INFO ] [openhab.model.script.GenUptime] - Getting total Uptime in msec
2015-10-20 14:41:47.101 [INFO ] [openhab.model.script.GenUptime] - Adding current time to total
2015-10-20 14:41:47.123 [ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Increment Generator Runtimes
java.lang.IllegalStateException: Could not invoke method: org.eclipse.xtext.xbase.lib.LongExtensions.operator_plus(long,long) on instance: null