Hi,
as your rule can trigger several times without changing the state, I would seperate the tracking into another rule. The variable declarations need to go to the top of the file below the import statements:
var DateTime RoofEnabled // time when heating was switched on
var DateTime RoofDisabled // time when heating was switched off
var long TimeEnabled // cumulative time the heating was on in [milliseconds]
var float Energy // Energy consumed by heating
var float Power = 1800 // Enter total power consumption by both plugs here [in Watts]
rule "Track RoofCable on off"
when
Item Roof changed // does this work with a group?? Expert knowledge needed here ;-)
then
if (Roof.state == ON) {
RoofEnabled = now
}
else {
RoofDisabled = now
TimeEnabled = TimeEnabled + RoofDisabled.millis - RoofEnabled.millis
Energy = TimeEnabled/1000 * Power // in Joule
Energy = Energy / 3600 // in kWh
}
end
Some initialization to avoid summing up of undefined values
rule "Startup init"
when
System started
then
RoofEnabled = now
RoofDisabled = now
TimeEnabled = 0
Energy = TimeEnabled/1000 * Power // in Joule
Energy = Energy / 3600 // in kWh
end
If you want to keep the Energy consumed over system restarts you need to persist the TimeEnabled variable and change the Startup rule accordingly. How to do that I have no idea, yet
I don’t know if the state works on groups. If not, you can change to the individual plug states.
was getting errors on data types with above (int vs number and similar).
got it to store a sample value in rrd4j but can’t get that to display in sitemap - assuming type mismatch.
current rule: rule "track roof state" when Item Roof changed then var long RoofCableStart var long RoofCableStop var long RoofCableTimeOn // var float RoofCableEnergy // var float RoofCablePower=1800 // sendMail("my@email","OpenHAB Roof","Roof Heat changed state to:" + Roof.state) if (Roof.state == ON) { RoofCableStart=now.millis } else { RoofCableStop=now.millis RoofCableTimeOn=RoofCableStop-RoofCableStart postUpdate(RoofTime, RoofCableTimeOn) // RoofCableEnergy=RoofCableTimeEnabled/1000*RoofCablePower // in Joule // RoofCableEnergy=RoofCableEnergy/3600 // in kWh } end
In sitemap I have: Text item=RoofCableTimeOn Text item=RoofTime
this is the log output: 12:41:30.981 [DEBUG] [m.r.internal.engine.RuleEngine:305 ] - Executing rule 'track roof state' 12:41:30.996 [DEBUG] [.p.rrd4j.internal.RRD4jService:132 ] - Stored 'RoofTime' with state '1448818890770' in rrd4j database 12:41:31.057 [DEBUG] [.p.rrd4j.internal.RRD4jService:113 ] - Stored 'Z_socket2' with state 'ON' in rrd4j database (again) 12:41:31.060 [DEBUG] [.p.rrd4j.internal.RRD4jService:132 ] - Stored 'Z_socket2' with state '0' in rrd4j database 12:41:31.100 [DEBUG] [.p.rrd4j.internal.RRD4jService:132 ] - Stored 'RoofTime' with state '1448818891014' in rrd4j database 12:41:31.221 [ERROR] [o.u.i.items.ItemUIRegistryImpl:438 ] - Cannot retrieve item RoofCableTimeOn for widget org.openhab.model.sitemap.Text 12:41:31.224 [ERROR] [o.u.i.items.ItemUIRegistryImpl:438 ] - Cannot retrieve item RoofCableTimeOn for widget org.openhab.model.sitemap.Text 12:41:31.226 [ERROR] [o.u.i.items.ItemUIRegistryImpl:438 ] - Cannot retrieve item RoofCableTimeOn for widget org.openhab.model.sitemap.Text 12:41:31.979 [DEBUG] [.p.rrd4j.internal.RRD4jService:132 ] - Stored 'Roof' with state '0' in rrd4j database
very close but getting an error with data types in openHAB designer:
“type mismatch: cannot convert from org.openhab.core.types.State to java.lang.Number”
on line: val long oldVal = if(RoofRuntimeMsec.state == Undefined || RoofRuntimeMsec.state == Uninitialized) 0 else (RoofRuntimeMsec.state as Number).longValue
below is log output looks like msec is ok but hours are not calculated
2015-11-30 21:21:34.024 [INFO ] [penhab.model.script.RoofUpTime] - Adding current time to total: timeSinceLastUpdate = 999 oldVal = 242992
2015-11-30 21:21:34.041 [INFO ] [penhab.model.script.RoofUpTime] - Current time hours = 0.0
2015-11-30 21:21:35.033 [INFO ] [penhab.model.script.RoofUpTime] - Adding current time to total: timeSinceLastUpdate = 1002 oldVal = 243991
2015-11-30 21:21:35.056 [INFO ] [penhab.model.script.RoofUpTime] - Current time hours = 0.0
var org.joda.time.DateTime whenStarted = null
rule "Roof started"
when
Item Roof received command ON
then
whenStarted = now
end
rule "Roof stopped"
when
Item Roof received command OFF
then
if(whenStarted != null) {
// do the same as the if(Roof.state == ON && whenStarted != null) in the increment rule
}
whenStarted = null
end
rule "Increment Roof Runtimes"
when
Time cron “* */1 * * * ?”
then
if(Roof.state == ON && whenStarted != null){
val long nowMsec = now.millis
val long wsMsec = whenStarted.millis
whenStarted = now
val long timeSinceLastUpdate = nowMsec - wsMsec
val long oldVal = if(RoofRuntimeMsec.state == Undefined || RoofRuntimeMsec.state == Uninitialized) 0 else (RoofRuntimeMsec.state as Number).longValue
logInfo(“RoofUpTime”, "Adding current time to total: timeSinceLastUpdate = " + timeSinceLastUpdate + " oldVal = " + oldVal)
val long totalMsec = oldVal + timeSinceLastUpdate // calculate total runtime
RoofRuntimeMsec.postUpdate(totalMsec) // post the full runtime
// val long hours = Math::round(totalMsec/1000/60/60)
val long temp = Math::round((totalMsec/100/60/60) * 10) // calculate the hours and move the decimal place before rounding to preserve the tenths place
val double hours = temp / 10.0 // move the decimal place back to get the tenth’s place, change the type to double so we don’t lose the decimal place
logInfo(“RoofUpTime”, "Current time hours = " + hours)
RoofRuntimeHours.postUpdate(hours)
}
else if(Roof.state == ON && whenStarted == null){
whenStarted = now
}
else {
just an update on this:
“val double hours = totalMsec/1000.0/60.0/60.0”
is now returning correct number of hours.
“Incompatible types. Expected double or java.lang.Double but was java.math.BigDecimal” and
“type mismatch: cannot convert from org.openhab.core.types.State to java.lang.Number”
are present in Designer but no errors in log and the rule is working.
thank you everyone for helping with this.
An Item carries a State which in turn carries the Number which you can get at using longValue, intValue, doubleValue, etc.
This is because by default the Rules engine casts all numerical values to BigDecimal. So if you do something like the following it should eliminate that warning:
val double hours = (totalMsec/1000.0/60.0/60.0).doubleValue
Rich thank you very much! This is now perfect and calculates time on and energy used in kWh.
This is the full working setup for anyone looking to do this in the future: ITEMS:
Number RoofRuntimeHours “Roof Cable Runtime [%.2f h]”// used on sitemap
Number RoofRuntimeMsec // used to store intermediate values which get rounded
Number RoofEnergy “Roof Energy used [%.2f kWh]”
SITEMAP:
Text item=RoofRuntimeHours icon=“selfEnergy”
Text item=RoofEnergy icon=“selfEnergy”
var org.joda.time.DateTime whenStarted = null
var Number RoofPower = 1650
rule "Roof started"
when
Item Roof received command ON
then
whenStarted = now
end
rule "Roof stopped"
when
Item Roof received command OFF
then
if(whenStarted != null) {
}
whenStarted = null
end
rule "Increment Roof Runtimes"
when
Time cron "* */1 * * * ?"
then
if(Roof.state == ON && whenStarted != null){
val long nowMsec = now.millis
val long wsMsec = whenStarted.millis
whenStarted = now
val long timeSinceLastUpdate = nowMsec - wsMsec
val long oldVal = if(RoofRuntimeMsec.state == Undefined || RoofRuntimeMsec.state == Uninitialized) 0 else (RoofRuntimeMsec.state as DecimalType).longValue
val long totalMsec = oldVal + timeSinceLastUpdate // calculate total runtime
RoofRuntimeMsec.postUpdate(totalMsec) // post the full runtime
val double hours = (totalMsec/1000.0/60.0/60.0).doubleValue
RoofRuntimeHours.postUpdate(hours)
RoofEnergy.postUpdate(hours*RoofPower/1000)
}
else if(Roof.state == ON && whenStarted == null){
whenStarted = now
}
else {
}
end
i do something similar, but different. (might not have if i saw this first though)
i’m logging details about when my water well pump is on.
rules of a similar nature determine how many seconds it was ON, at the time it goes OFF. this value, the time it was ON, gets persisted (mysql) at the time it went off, so i could determine when it went ON if i wanted, and more.
now, to calculate energy/kwhr for the last 30 days i do this
so this gives me total kWhr used in the last month.
(about $2/month to pump the water out of my well, unless i made some mistake here)
this way i can also retain all the information about when the pump went on and for how long (very interesting under drought conditions, how long it takes to refill my tanks)
likewise, to find out the total for one month, 2 months ago, say, i would calculate the total for the last two months and subtract out the previous month to leave the total for the month two months ago…
kWhr data is generated on the fly, not persisted. don’t know if i need to persist it or not, really.
i actually measure the pump current so could use .avgSince on the actual pump amperage to come up with an exact number for “7.0” but it never really changes very much.
This is a key point in your approach. Because of how rrd4j “complresses” data it might not be a suitable solution for this sort of data analysis.
Can’t think of a reason except if you wanted to chart it to perhaps see trends related to time of day and kWh usage. Or, if your rules might run at a time when your kWh Item is undefined it might be worthwhile to persist and restoreOnStartup.
probably many different options now but my setup is with
Aeotec by Aeon Labs ZW090 Z Stick Gen 5
GE Z-Wave Wireless Lighting Control Outdoor Module
Follow the z-wave setup tutorial for your version of OH and operating system (on a working system add zwave binding, modify config to assign correct serial port and I found HABmin to make life easier with setting up and monitoring zwave devices)
Unfortunately your rule @bakir is not working anymore in OH2.
It throws error:
[ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'Policz czas CWU': The name 'Undefined' cannot be resolved to an item or type; line 78, column 46, length 9
Even i changed to ‘Undefined’ and ‘Uninitialized’ there are still errors:
Error during the execution of rule 'Policz czas CWU': Could not cast NULL to org.eclipse.smarthome.core.library.types.DecimalType; line 78, column 110, length 35
Anyone has an idea how to fix it to have it working under OH2?
I did copy’n’paste from your post changing only names of rule and items.
Thanks, but there is still error Error during the execution of rule 'Policz czas CWU': Could not cast NULL to org.eclipse.smarthome.core.library.types.DecimalType; line 79, column 104, length 35
my rule look like this:
var org.joda.time.DateTime whenStarted = null
var Number cwuPower = 2000
rule "CWU start"
when
Item cwu received command ON
then
whenStarted = now
end
rule "CWU stop"
when
Item cwu received command OFF
then
if(whenStarted !== null){
}
whenStarted == null
end
rule "Policz czas CWU"
when
Time cron "* */1 * * * ?"
then
if(cwu.state == ON && whenStarted !== null){
val long nowMsec = now.millis
val long wsMsec = whenStarted.millis
whenStarted = now
val long timeSinceLastUpdate = nowMsec - wsMsec
val long oldVal = if(cwuRuntimeMsec.state == UNDEF || cwuRuntimeMsec.state == 'Uninitialized') 0 else (cwuRuntimeMsec.state as DecimalType).longValue
val long totalMsec = oldVal + timeSinceLastUpdate // calculate total runtime
cwuRuntimeMsec.postUpdate(totalMsec) // post the full runtime
val double hours = (totalMsec/1000.0/60.0/60.0).doubleValue
cwuRuntimeHours.postUpdate(hours)
cwuEnergy.postUpdate(hours*cwuPower/1000)
}
else if(cwu.state == ON && whenStarted == null){
whenStarted = now
}
else {
}
end
error is referencing to this line: val long oldVal = if(cwuRuntimeMsec.state == UNDEF || cwuRuntimeMsec.state == 'Uninitialized') 0 else (cwuRuntimeMsec.state as DecimalType).longValue
rule "Increment Roof Runtimes"
when
Time cron "* */1 * * * ?"
then
if(Sonoff07.state == ON && whenStarted !== null){
val long nowMsec = now.millis
val long wsMsec = whenStarted.millis
whenStarted = now
val long timeSinceLastUpdate = nowMsec - wsMsec
val long oldVal = if(RoofRuntimeMsec.state == UNDEF) 0 else (RoofRuntimeMsec.state as DecimalType).longValue
val long totalMsec = oldVal + timeSinceLastUpdate // calculate total runtime
RoofRuntimeMsec.postUpdate(totalMsec) // post the full runtime
val double hours = (totalMsec/1000.0/60.0/60.0).doubleValue
RoofRuntimeHours.postUpdate(hours)
RoofEnergy.postUpdate(hours*RoofPower/1000)
}
else if(Sonoff07.state == ON && whenStarted === null){
whenStarted = now
}
else {
}
end
but I have an error on line
val long oldVal = if(RoofRuntimeMsec.state == UNDEF) 0 else (RoofRuntimeMsec.state as DecimalType).longValue
the error is:
2019-02-28 11:32:47.027 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'Increment Roof Runtimes': Could not cast NULL to org.eclipse.smarthome.core.library.types.DecimalType; line 267, column 114, length 36
I manage to use this code for a TV, which is plugged in a strip.
Now I want to find a solution for all 3 sockets from the strip. Can this be done with members? Or should i create 2 times more the rule?
my rule:
import java.lang.Math
var org.joda.time.DateTime whenStarted = null
rule “LGTV started”
when
Item TellurPowerStripTellurPower1 received command ON
then
whenStarted = now
end
rule “LGTV stopped”
when
Item TellurPowerStripTellurPower1 received command OFF
then
if(whenStarted != null) {
// do the same as the if(TellurPowerStripTellurPower1.state == ON && whenStarted != null) in the increment rule
}
whenStarted = null
end
rule “Increment LGTV Runtimes”
when
Time cron “* */1 * * * ?”
then
if(TellurPowerStripTellurPower1.state == ON && whenStarted != null){
logInfo(“GenUptime”, “LGTV: Getting now”)
val long nowMsec = now.millis
logInfo("GenUptime", "LGTV: Getting whenStarted")
val long wsMsec = whenStarted.millis
logInfo("GenUptime", "LGTV: Resetting whenStarted")
whenStarted = now
logInfo("GenUptime", "LGTV: Calculating time difference")
val long timeSinceLastUpdate = nowMsec - wsMsec
logInfo("GenUptime", "LGTV: Getting total Uptime in msec")
//val long oldVal = TellurPowerStripTellurPower1RuntimeMSec.state
val long oldVal = if(TellurPowerStripTellurPower1RuntimeMSec.state === NULL) 0 else (TellurPowerStripTellurPower1RuntimeMSec.state as Number).longValue
//logInfo("GenUptime", "Adding current time to total")
logInfo("GenUpTime", "LGTV: Adding current time to total: timeSinceLastUpdate = " + timeSinceLastUpdate + " oldVal = " + oldVal)
val long totalMsec = oldVal + timeSinceLastUpdate // calculate total runtime
logInfo("GenUptime", "LGTV: Posting totalMsec to Item")
TellurPowerStripTellurPower1RuntimeMSec.postUpdate(totalMsec) // post the full runtime
logInfo("GenUptime", "LGTV: Calculating hours")
//val long hours = Math::round(totalMsec/1000/60/60)
val double hours = totalMsec/1000.0/60.0/60.0
logInfo("GenUptime", "LGTV: Posting hours")
TellurPowerStripTellurPower1RuntimeHours.postUpdate(hours)
logInfo("GenUptime", "LGTV: Calculate energy consumption")
var float LGTVPower = 130 // Enter total power consumption [in Watts]
val double LGTVEnergyConsumption = hours * LGTVPower / 1000 // Energy consumed [kWh] Energy = Power * Time
LGTVEnergy.postUpdate(LGTVEnergyConsumption)
}
else if(TellurPowerStripTellurPower1.state == ON && whenStarted == null){
logInfo("GenUptime", "LGTV: TV is ON but 'LGTV started' rule never executed")
whenStarted = now
}
else {
logInfo("GenUptime", "LGTV is not running")
}