Generator hours counter

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?

2 Likes

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.

1 Like

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