Counting ON Time for Heater

Continuing the discussion from Count ON time, sometimes negative value:
from @rlkoshak
I read through that post and @rlkoshak response and am trying to get that to run. I like the compact rule but not having luck with it.

items

Number:time heater_Runtime_Total "Heater Runtime Total [%1$tH:%1$tM:%1$tS]" <time>
DateTime heater_Start_Time
DateTime heater_End_Time

rule

var startTime = heater_Start_Time.state
var endTime = heater_End_Time.state

var totalRuntime = 0

//Calculate Heater Runtime

rule "Calculate Heater Runtime"
when
    Item MainHeater changed
then
if(heater_Runtime_Total instanceof QuantityType){
    totalRuntime = (heater_Runtime_Total.state as QuantityType).toUnit("s").intValue // get the time in number of seconds
}

if(newState == OFF){
    endTime = now
    heater_End_Time.postUpdate(endTime)
}
else if(newState == ON){
    startTime = now
    heater_Start_Time.postUpdate(startTime)
}
else {
    return;
}

if(startTime.isBefore(endTime)){
    var runtime = java.time.Duration.between(startTime, endTime)
    logInfo("Heater", "Heater Duration in Minuten - " + runtime.toMinutes)
    totalRuntime = totalRuntime + runtime.toSeconds
    heater_Runtime_Total.postUpdate(totalRuntime.toString+" s")
}
end

multiple errors
When I load the file I get this.

Validation issues found in configuration model 'heater_runtime_two.rules', using it anyway:
Cannot reference the field 'heater_Start_Time' before it is defined
Cannot reference the field 'heater_Start_Time' before it is defined
Cannot reference the field 'heater_Start_Time' before it is defined
Cannot reference the field 'heater_End_Time' before it is defined
Cannot reference the field 'heater_End_Time' before it is defined
Cannot reference the field 'heater_End_Time' before it is defined
QuantityType is a raw type. References to generic type QuantityType<T> should be parameterized

when it runs

 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'heater_runtime_two-1' failed: An error occurred during the script execution: Could not invoke method: org.openhab.core.model.script.actions.BusEvent.postUpdate(org.openhab.core.items.Item,org.openhab.core.types.State) on instance: null in heater_runtime_two

assuming there is a null value in there that needs to be defined first?
also, maybe a stupid question but do all the variables need to be defined as items?

thanks!

Check your xxx.items file has loaded, it should leave an announcement in your openhab.log

Variables within rules, and Items as key openHAB components, are very different things (small t).

I do get this when I load the item file.

[INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'greenhouse.items'

Ah, the penny has just dropped.
You can’t do that assignment as global variables, there is something strange about Item states in that particular context at rules load time.

But it any case, you don’t want to - that would assign the variable as the Item state at the time the rules file loaded. Just at that moment, and never again - a static evaluation.
You’d be wanting to know the states at the time the rule ran, I think?

If you must pre-populate those variables (I can’t see why) you can do so in a ‘System started’ triggered rule.

But there are other difficulties here when you run for the very first time. You need to take account of stuff that has not been initialized.
You can’t get the NULL state of an Item as an integer, for example.
You need to write code that deals with that situation.

If you don’t use these variable anywhere else, don’t have them as globals unnecessarily.

rule "Calculate Heater Runtime"
when
    Item MainHeater changed
then
    // get the current variable states
    // but what if we've never run before?
    var startTime = now
    if ( !(heater_Start_Time.state instanceof UndefType) ) {  // not NULL or UNDEF
        startTime = heater_Start_Time.state
    }    
    var endTime = now
    if ( !(heater_End_Time.state instanceof UndefType) ) { 
        endTime = heater_end_Time.state
    }    

    var totalRuntime = 0
    if (heater_Runtime_Total.state instanceof QuantityType) {  // it's the STATE of interest
        totalRuntime = (heater_Runtime_Total.state as QuantityType).toUnit("s").intValue // get the time in number of seconds
    }  // but what if it wasn't a Quantity?  That's okay, variable stays zero


// rest of your code

Ok, getting closer!

Get the following error:

Validation issues found in configuration model 'heater_runtime_three.rules', using it anyway:
QuantityType is a raw type. References to generic type QuantityType<T> should be parameterized

parameterized! is that word? :grinning:

rule "Calculate Heater Runtime"
when
    Item MainHeater changed
then
    // get the current variable states
    // but what if we've never run before?
    var startTime = now
    if ( !(heater_Start_Time.state instanceof UndefType) ) {  // not NULL or UNDEF
        startTime = heater_Start_Time.state
    }    
    var endTime = now
    if ( !(heater_End_Time.state instanceof UndefType) ) { 
        endTime = heater_End_Time.state
    }    

    var totalRuntime = 0
    if (heater_Runtime_Total.state instanceof QuantityType) {  // it's the STATE of interest
        totalRuntime = (heater_Runtime_Total.state as QuantityType).toUnit("s").intValue // get the time in number of seconds
    }  // but what if it wasn't a Quantity?  That's okay, variable stays zero


// rest of your code

if(MainHeater == OFF){
    endTime = now
    heater_End_Time.postUpdate(endTime)
}
else if(MainHeater == ON){
    startTime = now
    heater_Start_Time.postUpdate(startTime)
}
else {
    return;
}

if(startTime.isBefore(endTime)){
    var runtime = java.time.Duration.between(startTime, endTime)
    logInfo("Heater", "Heater Duration in Minutes - " + runtime.toMinutes)
    totalRuntime = totalRuntime + runtime.toSeconds
    heater_Runtime_Total.postUpdate(totalRuntime.toString+" s")
}
end

The Quantity type needs qualifying
as QuantityType<Number>
Should do generic I think
Or might need specific <Time>

I think it might work if you go totally generic.

as QuantityType<*>

If you do need to use Time, you’ll have to import it and I’m not sure I can find where to import it from right now. It’s always a pain to find because some units are created by the OH project and others are maintained by the upstream library.

Now I recall there is something a little odd about this one Quantity

I tried generic with <*>. It does not like that…

 Configuration model 'heater_runtime_three.rules' has errors, therefore ignoring it: [17,60]: no viable alternative at input '*'
rule "Calculate Heater Runtime"
when
    Item MainHeater changed
then
    // get the current variable states
    // but what if we've never run before?
    var startTime = now
    if ( !(heater_Start_Time.state instanceof UndefType) ) {  // not NULL or UNDEF
        startTime = heater_Start_Time.state
    }    
    var endTime = now
    if ( !(heater_End_Time.state instanceof UndefType) ) { 
        endTime = heater_End_Time.state
    }    

    var totalRuntime = 0
    if (heater_Runtime_Total.state instanceof QuantityType<*>) {  // it's the STATE of interest
        totalRuntime = (heater_Runtime_Total.state as QuantityType<*>).toUnit("s").intValue // get the time in number of seconds
    }  // but what if it wasn't a Quantity?  That's okay, variable stays zero

if(MainHeater == OFF){
    endTime = now
    heater_End_Time.postUpdate(endTime)
}
else if(MainHeater == ON){
    startTime = now
    heater_Start_Time.postUpdate(startTime)
}
else {
    return;
}

if(startTime.isBefore(endTime)){
    var runtime = java.time.Duration.between(startTime, endTime)
    logInfo("Heater", "Heater Duration in Minutes - " + runtime.toMinutes)
    totalRuntime = totalRuntime + runtime.toSeconds
    heater_Runtime_Total.postUpdate(totalRuntime.toString+" s")
}
end

Did not like either:

Validation issues found in configuration model 'heater_runtime_three.rules', using it anyway:
Cannot perform instanceof check against parameterized type QuantityType<Number>
Cannot perform instanceof check against parameterized type QuantityType<Number>
Cannot perform instanceof check against parameterized type QuantityType<Number>

And

Configuration model 'heater_runtime_three.rules' has errors, therefore ignoring it: [18,60]: no viable alternative at input 'Time'
[19,68]: no viable alternative at input 'Time'

Perhaps we should have made it clearer that the thing to change would be the thing reporting the error.

if (heater_Runtime_Total.state instanceof QuantityType) {

Leave that one alone. It doesn’t care what flavour of QuantityType.

totalRuntime = (heater_Runtime_Total.state as QuantityType<DateTime>).toUnit("s").intValue
or
totalRuntime = (heater_Runtime_Total.state as QuantityType<Number>).toUnit("s").intValue

This is the one that needs guidance

Got it. I made the changes with both and . Both will load with no errors but neither seem to produce any results. I added some logging (maybe not very well) but i get nothing when the heater turns on and the rule fires. Here’s the updated along with the items just to validate.

Items

Number:Time heater_Runtime_Total "Heater Runtime Total [%1$tH:%1$tM:%1$tS]" <time>
DateTime heater_Start_Time
DateTime heater_End_Time

rule

//Calculate Heater Time
rule "Calculate Heater Runtime"
when
    Item MainHeater changed
then
    // get the current variable states
    // but what if we've never run before?
    var startTime = now
    if ( !(heater_Start_Time.state instanceof UndefType) ) {  // not NULL or UNDEF
        startTime = heater_Start_Time.state

    }    
    var endTime = now
    if ( !(heater_End_Time.state instanceof UndefType) ) { 
        endTime = heater_End_Time.state
    }    

    var totalRuntime = 0
    if (heater_Runtime_Total.state instanceof QuantityType) {  // it's the STATE of interest
        totalRuntime = (heater_Runtime_Total.state as QuantityType<Number>).toUnit("s").intValue // get the time in number of seconds
    }  // but what if it wasn't a Quantity?  That's okay, variable stays zero


// rest of your code

if(MainHeater == OFF){
    endTime = now
    heater_End_Time.postUpdate(endTime)
}
else if(MainHeater == ON){
    startTime = now
    heater_Start_Time.postUpdate(startTime)
	logInfo("main heater start time", "Main Heater Start Time :"+heater_Start_Time.state.toString)
}
else {
    return;
}

if(startTime.isBefore(endTime)){
    var runtime = java.time.Duration.bet/ween(startTime, endTime)
    logInfo("Heater", "Heater Duration in Minutes - " + runtime.toMinutes)
    totalRuntime = totalRuntime + runtime.toSeconds
    heater_Runtime_Total.postUpdate(totalRuntime.toString+" s")
	logInfo("Greenhouse Heater Total Time",heater_Runtime_Total.state)
}
end

Trying to log the total time but nothing shows in the log. it shows that heater turns ON and OFF when I switch it on and off but nothing logs.

thanks!

C’mon, you know this stuff by now, and have a reminder comment in your rule already.
Items have labels, types, blah … .state is the interesting one?