Rule to monitor device status ON/OFF and calculating total usage

If you could post everything in the rules file, not just a single rule, it might be easier to help spot the problem.:wink:

Good suggestion. Attached my entire file. There is not a lot there at the moment :wink:

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*

var Number counter = 1
var Timer timer = null

// Creates an item that stores the last update time of this item
rule "Records last weather update time"
when
  Item Weather_Temperature received update
then
  postUpdate(Weather_LastUpdate, new DateTimeType())
end

 rule "Initialize Location"
	when 
		System started
	then
		DemoLocation.postUpdate(new PointType ("51.44666,-0.85117"))
end

rule "Set daily max and min temperature" 
when 
     Item Weather_Temperature changed or 
     Time cron "0 0 0 * * ?" or 
     System started 
then 
     val max = Weather_Temperature.maximumSince(now.withTimeAtStartOfDay) 
     val min = Weather_Temperature.minimumSince(now.withTimeAtStartOfDay) 
     if (max !== null && min !== null) { 
         postUpdate(Weather_Temp_Max, max.state) 
         postUpdate(Weather_Temp_Min, min.state) 
     } 
end 

/** shows how to use sensor values from the past */
rule "Persistence Demo"
when
	Time cron "0 * * * * ?"
then	
	if(Weather_Temperature.changedSince(now.minusMinutes(1))) {
		logInfo("PersistenceDemo", "2 minutes ago, the temperature was " + Weather_Temperature.historicState(now.minusMinutes(2)) + " degrees.")
	}
end


// Creates an item that stores the last update time of this item
rule "Records last weather update time"
when
  Item Weather_Temperature received update
then
  postUpdate(Weather_LastUpdate, new DateTimeType())
end


// This rule will be used to test Scale transformation service
rule "Compute humidex"
when
    Item Weather_Temperature changed or
	Item Weather_Humidity changed
then
	var Number T = Weather_Temperature.state as DecimalType
	var Number H = Weather_Humidity.state as DecimalType	
	var Number x = 7.5 * T/(237.7 + T)
	var Number e = 6.112 * Math::pow(10, x.doubleValue) * H/100
	var Number humidex = T + (new Double(5) / new Double(9)) * (e - 10)
	postUpdate(Weather_Humidex, humidex)
end


// Switch LGTVStatus "TV State" <switch> - Item Definition (items) 
// Number LGTVStatus_Start_Time "TV Start Time [%d]" - Item Definition (items) - Stores Epoch timestamp 
// Number LGTVStatus_Stop_Time "TV Stop Time [%d]" - Item Definition (items) - Stores Epoch timestamp 
// Number LGTVStatus_Runtime_Minutes "TV Runtime Minutes [%d]" Item Definition (items) - Stores runtime minutes 

// rule "TV State" 
// when 
//    Item LGTVStatus changed 
// then
//    if (LGTVStatus.state == 0) LGTVStatus.postUpdate(OFF) 
//    if (LGTVStatus.state > 0) LGTVStatus.postUpdate(ON) 
// end

var Number Epoch_Time = 0 
var Number LGTVStatus_Runtime = 0 

rule "Calculate TV Runtime" 
when
    Item LGTVStatus changed
	then Epoch_Time = now.millis
	if (LGTVStatus.state == OFF) LGTVStatus_Stop_Time.postUpdate(Epoch_Time)
	if (LGTVStatus.state == ON) LGTVStatus_Start_Time.postUpdate(Epoch_Time)
	if ((LGTVStatus_Start_Time.state as DecimalType) > 0 && (LGTVStatus_Stop_Time.state as DecimalType) > 0) { 
	     LGTVStatus_Runtime = ((LGTVStatus_Stop_Time.state as DecimalType) - (LGTVStatus_Start_Time.state as DecimalType)) / 60000
         LGTVStatus_Runtime_Minutes.postUpdate(LGTVStatus_Runtime)
         logInfo("LGTV", "LGTV Runtime - " + LGTVStatus_Runtime)
		 }
end




var Number Epoch_Time = 0 
var Number LGTVStatus_Runtime = 0 

Try moving the two global var’s to the top of the rules file or place them inside the rule.

This removes the error as long as you don’t need those two var’s in the rules above.

rule "Calculate TV Runtime" 
when
    Item LGTVStatus changed
	then
	var Number Epoch_Time = 0
        var Number LGTVStatus_Runtime = 0 
 
	Epoch_Time = now.millis
	if (LGTVStatus.state == OFF) { LGTVStatus_Stop_Time.postUpdate(Epoch_Time) }
	if (LGTVStatus.state == ON) { LGTVStatus_Start_Time.postUpdate(Epoch_Time) }
	if ((LGTVStatus_Start_Time.state as DecimalType) > 0 && (LGTVStatus_Stop_Time.state as DecimalType) > 0) { 
	     LGTVStatus_Runtime = ((LGTVStatus_Stop_Time.state as DecimalType) - (LGTVStatus_Start_Time.state as DecimalType)) / 60000
         LGTVStatus_Runtime_Minutes.postUpdate(LGTVStatus_Runtime)
         logInfo("LGTV", "LGTV Runtime - " + LGTVStatus_Runtime)
		 }
end

If you need the var’s this also removes the error:

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*

var Number counter = 1
var Timer timer = null
var Number Epoch_Time = 0
var Number LGTVStatus_Runtime = 0 

// Creates an item that stores the last update time of this item
rule "Records last weather update time"
when
  Item Weather_Temperature received update
then
  postUpdate(Weather_LastUpdate, new DateTimeType())
end

 rule "Initialize Location"
	when 
		System started
	then
		DemoLocation.postUpdate(new PointType ("51.24626,-0.75117"))
end

rule "Set daily max and min temperature" 
when 
     Item Weather_Temperature changed or 
     Time cron "0 0 0 * * ?" or 
     System started 
then 
     val max = Weather_Temperature.maximumSince(now.withTimeAtStartOfDay) 
     val min = Weather_Temperature.minimumSince(now.withTimeAtStartOfDay) 
     if (max !== null && min !== null) { 
         postUpdate(Weather_Temp_Max, max.state) 
         postUpdate(Weather_Temp_Min, min.state) 
     } 
end 

/** shows how to use sensor values from the past */
rule "Persistence Demo"
when
	Time cron "0 * * * * ?"
then	
	if(Weather_Temperature.changedSince(now.minusMinutes(1))) {
		logInfo("PersistenceDemo", "2 minutes ago, the temperature was " + Weather_Temperature.historicState(now.minusMinutes(2)) + " degrees.")
	}
end


// Creates an item that stores the last update time of this item
rule "Records last weather update time"
when
  Item Weather_Temperature received update
then
  postUpdate(Weather_LastUpdate, new DateTimeType())
end


// This rule will be used to test Scale transformation service
rule "Compute humidex"
when
    Item Weather_Temperature changed or
	Item Weather_Humidity changed
then
	var Number T = Weather_Temperature.state as DecimalType
	var Number H = Weather_Humidity.state as DecimalType	
	var Number x = 7.5 * T/(237.7 + T)
	var Number e = 6.112 * Math::pow(10, x.doubleValue) * H/100
	var Number humidex = T + (new Double(5) / new Double(9)) * (e - 10)
	postUpdate(Weather_Humidex, humidex)
end


// Switch LGTVStatus "TV State" <switch> - Item Definition (items) 
// Number LGTVStatus_Start_Time "TV Start Time [%d]" - Item Definition (items) - Stores Epoch timestamp 
// Number LGTVStatus_Stop_Time "TV Stop Time [%d]" - Item Definition (items) - Stores Epoch timestamp 
// Number LGTVStatus_Runtime_Minutes "TV Runtime Minutes [%d]" Item Definition (items) - Stores runtime minutes 

// rule "TV State" 
// when 
//    Item LGTVStatus changed 
// then
//    if (LGTVStatus.state == 0) LGTVStatus.postUpdate(OFF) 
//    if (LGTVStatus.state > 0) LGTVStatus.postUpdate(ON) 
// end


rule "Calculate TV Runtime" 
when
    Item LGTVStatus changed
	then
	Epoch_Time = now.millis
	if (LGTVStatus.state == OFF) { LGTVStatus_Stop_Time.postUpdate(Epoch_Time) }
	if (LGTVStatus.state == ON) { LGTVStatus_Start_Time.postUpdate(Epoch_Time) }
	if ((LGTVStatus_Start_Time.state as DecimalType) > 0 && (LGTVStatus_Stop_Time.state as DecimalType) > 0) { 
	     LGTVStatus_Runtime = ((LGTVStatus_Stop_Time.state as DecimalType) - (LGTVStatus_Start_Time.state as DecimalType)) / 60000
         LGTVStatus_Runtime_Minutes.postUpdate(LGTVStatus_Runtime)
         logInfo("LGTV", "LGTV Runtime - " + LGTVStatus_Runtime)
		 }
end

Thanks @H102 by moving both var`s to the top of the rule this resolve issue with previous warning (missing EOF).

Looking at openhab log file it is showing me some additional validation issues but (INFO) not sure if this is anything to worry about.

2018-12-25 16:10:38.145 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'home.rules', using it anyway:
The use of wildcard imports is deprecated.
The use of wildcard imports is deprecated.
The use of wildcard imports is deprecated.
2018-12-25 16:10:38.153 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'home.rules'
2018-12-25 16:18:16.454 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'home.rules', using it anyway:
The use of wildcard imports is deprecated.
The use of wildcard imports is deprecated.
The use of wildcard imports is deprecated.
2018-12-25 16:18:16.584 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'home.rules'

But I still don’t get any calculations out of this rule.

Is my syntax correct ?

Sitemap

Text item=LGTVStatus_Runtime_Minutes label="Runtime Time [%d]"

Items

Number LGTVStatus_Runtime_Minutes "TV Runtime Minutes"

Should I set anything in rrd4j ? So far I only been adjusting rule file, sitemap & items.

This error is from:

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*

the * in the imports.

Do you need the imports? Most rule’s with OH run without calling for imports. Maybe try the rule without and see if it still runs or try removing the *.

I didn’t really study the rule to much, wrapping paper is everywhere.:laughing:

If you are trying to compare a current value to an older one then yes you will need to add the items to the persist file.

Example:

Strategies {
	// for rrd chart cron strategy every minute
	everyMinute : "0 * * * * ?"
	// get data reduced for older values to keep database small
	everyHour : "0 0 * * * ?"
	everyDay : "0 0 0 * * ?"

	default = everyChange
}

Items {
LGTVStatus_Runtime_Minutes : strategy = everyUpdate, everyMinute, restoreOnStartup
* : strategy = everyUpdate, everyMinute, restoreOnStartup
}

You can list all item separate or use the * to add all of them like the second line in items above. See the doc’s for more details and examples.

Thanks. At the moment my goal is to display total usage time in one day as bare minimum. Later will try to expand it to week/month & year primarily for internal analysis only :wink:

I just discovered some errors in log file with regards to rule.

2018-12-25 16:47:44.282 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Calculate TV Runtime': The name 'LGTVStatus_Stop_Time' cannot be resolved to an item or type; line 91, column 31, length 20
2018-12-25 16:48:31.928 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'home.sitemap', using it anyway:
Sitemap should contain either only frames or none at all
2018-12-25 16:48:31.964 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'home.sitemap'
2018-12-25 16:49:47.439 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Calculate TV Runtime': The name 'LGTVStatus_Start_Time' cannot be resolved to an item or type; line 92, column 30, length 21
2018-12-25 16:51:47.165 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'home.rules', using it anyway:
The use of wildcard imports is deprecated.
The use of wildcard imports is deprecated.
The use of wildcard imports is deprecated.
2018-12-25 16:51:47.268 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'home.rules'

It doesn’t like ‘LGTVStatus_Stop_Time’ & ‘LGTVStatus_Start_Time’

Those two should be virtual items created by you.

You can forget the persistence for now. Main goal is to get your this rule running. When you do so, you can set up a second rule to reset the counter item at midnight to see daily calculations.

I tried replacing LGTVStatus with another running item at home (light switch) but there is still nothing being populated in this rule. Should I be restarting openhab after updating rule file ?

I seen this topic earlier , probably with slightly easier rule and Vincent solution to increment counter. Will try test it quickly to see if it works.

A restart is not needed.

And yes, you can experiment with something more simple.

Do you know what a virtual item is? You don’t have to replace your tv status item with some other actual item that is linked to a physical switch. Just one “test” item.

I gather the idea of virtual item. I will try to set up test switch which I could toggle freely and will see if it works. There must be some fundamental mistake in my syntax somewhere.

1 Like

Hi All,

I had to abandon previous rule as I could not get it to work no matter what I tried :wink:.
Found another rule on this forum implementing counter function and after linking to my test device (switchable device for now) it works.

Below you can find the current code which seems to be working for me at the moment.

rule "LGTVStatus counter"
when
  // Every minute, 5 seconds after the minute
  Time cron "5 * * ? * * *"
then
  if (LGTVStatus.state == ON) {
    LGTVStatus_Time_Active.postUpdate((LGTVStatus_Time_Active.state as Number) + 1)
    if (LGTVStatus_Time_Day.state != NULL) {
      LGTVStatus_Time_Day.postUpdate((LGTVStatus_Time_Day.state as Number) + 1)
    }
  } else {
    LGTVStatus_Time_Active.postUpdate(0)
  }
end

I will give it a go to see how reliable this code will be.

So as this works for now my next step will be to reset this counter at midnight each day and move accumulated value to another item called LGTVStatus_Time_Day to display total usage value from previous day.

How would I do this ?

I think this should work if I could trigger Midnight_Sync item somehow

rule "Reset today value and sync"
when
  Item Midnight_Sync received command
then
  if (Midnight_Sync) {
    LGTVStatus_Time_Day.postUpdate(0)
  }
end

Also, how to ensure that this data is not lost during day for any reason (openhab restart for example).
Ideally I would like to be able to store this data and then display a device total usage for duration of:

  1. One day
  2. A Week
  3. A Month
  4. A Year

Regarding the following:

You are actually going to need to setup persistence to achieve your goal.

So as this works for now my next step will be to reset this counter at midnight each day and move accumulated value to another item called LGTVStatus_Time_Day to display total usage value from previous day.

How would I do this ?

rule "Reset Counter"
when
    time cron "0 0 0 * * ?"  // Fires midnight every day
then
    [do some stuff here]
end```

Regards,
Burzin

Thanks Burzin,

I think I got confused with this code earlier or maybe overcomplicating it.

Just to clarify what I looking to achieve.

  1. When the device is switched ON, LGTVStatus_Time_Active will be showing time in minutes since the unit was switched ON last time.

  2. When the device is being switched OFF , LGTVStatus_Time_Active should clear to 0 and accumulated value should be copied to my daily total run time - LGTVStatus_Time_Day. This value should be available on display throughout the day at any time (till midnight).
    At the moment LGTVStatus_Time_Day is showing 0 at all the time.

if (LGTVStatus.state == OFF) { move value from LGTVStatus_Time_Active to LGTVStatus_Time_Day 

NB: not exactly sure how to code this above section.

  1. At midnight LGTVStatus_Time_Day should reset to 0. I think I will need to create another register for that to capture data from previous day - LGTVStatus_Time_YDay (yesterday) ?

This probably would work for last two days but if I would like to gather further information (for the last week or month) it is not very clear for me how to prepare this at the moment. I think this would be very messy if I carry on like this.

Am I trying to understand correctly ?

NB:
I tried to use this rule to reset counter but as soon I add to my rule file it will freeze operation of counter so I had to remove from it for time being.

rule "Reset Daily Counter"
when
    time cron "0 0 0 * * ?"  // Fires midnight every day
then
    LGTVStatus_Time_Day.postUpdate(0)
end

Hi Andy,

My openHAB installation just blew up on me while setting up persistence, so I don’t know how much help I can be, but I’ll try.

Item 1

I think you have this covered.

Items 2 and 3

To me it seems like the best way to accomplish this is to modify the rule in item 1. A line like this may work

if (now.getHourOfDay == 0 && now.getMinuteOfHour == 0) {  //ie Midnight 
    [Do something]
}

I think you are right that it will get messy, but you may be able to simplify a bit bit by doing additional rollovers in additional rules. If example, imagine an item to hold to week of the year . (item = cal.get(WEEK_OF_YEAR)) then you could act upon a change to that item.

Regards,
Burzin

Need to use capitol letter for time.

when
    Time cron "0 0 0 * * ?"  // Fires midnight every day
then

@H102 - well spotted, capital letter resolved the problem, I forget everything is case sensitive :wink:

Graphic illustration what I can see at the moment regarding time ON

Test

Position one “ON Time” is showing minutes elapsed since the unit was initially switched ON. As you can see it was 11 minutes ago. This is linked to LGTVStatus_Time_Active in my counter rule.

Line below “Daily ON Time” is linked to LGTVStatus_Time_Day and should be showing sum of ON Time values accumulated during a day. So every time TV is switched off I will need to move ON TIME value to DAILY ON TIME somehow. I will see how I can use this syntax provide by @Burzin_Sumariwalla

As per my post above then at midnight DAILY ON TIME should be copied over to another register ,lets called it YDAY ON Time and then counter should reset to 0.

This would give me a partial solution in response to my initial design, still not sure how to capture entire week & month. Perhaps chart could be used.

Setting persistence it will be a challenge for me :wink:

 LGTVStatus_Time_Day = LGTVStatus_Time_Active.state as Number


Just to update you all on the situation. To my surprise this morning Daily ON Time seems to be working correctly now so its good news. It looks like the problem was with counter reset as I had it set to midnight and it wouldn`t work without this being set to 0 first :grinning:
Now when the TV is switched ON, the “ON Time” will be showing minutes elapsed since the unit was switched ON. When the device goes OFF “ON Time” counter will reset to 0 and then restarts as soon TV is switched ON again. “Daily ON Time” will capture a sum of every “ON Time” instances as expected.

Next challenge will be populate this data to show daily/weekly and monthly usage. Perhaps chart is the best way to achieve it?

Evening All,

I have 3 devices which are being monitored at the moment. I used the same rule for all three devices to calculate runtime but weirdly only one rule works well - calculates MyDevice1_Time_Active & MyDevice1_Time_Day properly.
Below is the copy of this rule.

rule "Device 1 counter"
when
  // Every minute, 5 seconds after the minute
  Time cron "5 * * ? * * *"
then
  if (MyDevice1.state == ON) {
    MyDevice1_Time_Active.postUpdate((MyDevice1_Time_Active.state as Number) + 1)
  if (MyDevice1_Time_Day.state != NULL) {
    MyDevice1_Time_Day.postUpdate((MyDevice1_Time_Day.state as Number) + 1)
    }
  } else {
    MyDevice1_Time_Active.postUpdate(0)
  }
end


rule "Reset Daily Counter"
when
    Time cron "0 0 0 * * ?"  // Fires midnight every day
then
    MyDevice1_Time_Day.postUpdate(0)
end

For the remaining two devices I used the same syntax , only changed the device name really.
The rule will calculate LGTVStatus_Time_Active but not LGTVStatus_Time_Day.
Can anyone anyone spot any issues with the second rule as I can`t find anything which could stop rule operation. I did worked for a day (was accumulating total value during day) but then it stopped without any reason and not sure why :thinking:

rule "LGTVStatus counter"
when
  // Every minute, 5 seconds after the minute
  Time cron "5 * * ? * * *"
then
  if (LGTVStatus.state == ON) {
    LGTVStatus_Time_Active.postUpdate((LGTVStatus_Time_Active.state as Number) + 1)
    if (LGTVStatus_Time_Day.state != NULL) {
    LGTVStatus_Time_Day.postUpdate((LGTVStatus_Time_Day.state as Number) + 1)
    }
  } else {
    LGTVStatus_Time_Active.postUpdate(0)
  }
end


rule "Reset Daily Counter"
when
    Time cron "0 0 0 * * ?"  // Fires midnight every day
then
    LGTVStatus_Time_Day.postUpdate(0)
end

Identical rule names? You can combine all reset rules into one:

rule "Reset Daily Counter"
when
    Time cron "0 0 0 * * ?"  // Fires midnight every day
then
    LGTVStatus_Time_Day.postUpdate(0)
    Second counter to reset...
    Third counter to reset... 
    ... 
end