Hi all. just started using OpenHAB and loving it so far even with my limited coding skills.
I have two z-wave switches controlling a heating cable on my roof and would like to track time they have been on and energy used by them (power is a constant of 1800W).
I cannot figure out how to track number of hours a switch/item has been on so any help would be appreciated.
this is my current rule (Roof is a group containing two switches):
rule "roof cable on" when Item RoofCable changed or Item Temperature changed then if ((RoofCable.state == ON && Temperature.state <49 && Temperature.state >37)) { sendCommand(Roof, ON) } else sendCommand(Roof, OFF) end
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
There is a long thread here where I helped someone implement just such a counter for a generator.
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
full rule:
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import java.util.*
import java.text.SimpleDateFormat
import java.lang.Math
import org.joda.time.*
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 {
}
end
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.
This should be:
(RoofRuntimeMsec.state as DecimalType).longValue
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â
RULE:
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import java.util.*
import java.text.SimpleDateFormat
import java.lang.Math
import org.joda.time.*
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
ftmp = PumpFillOnTime.sumSince(now.minusDays(30)).floatValue
so i add up all the seconds itâs been on in the last month.
i know the pump is 220VAC and uses 7.0 amps, and using 20cents/kWhr as a guess
// Volts * Amps * kW * $/kW
ftmp = ((220.0 * 7.0) / 1000.0) * 0.2 * ftmp / 3600.0
ftmp = Math::round(ftmp.floatValue*100.0)/100.0
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.
Dear bakir, which z-wave devices you used? How to install and connect them to OH ? Can you show me some tutorials?
Hi,
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.
Use UNDEF instead of Undefined. This is one of the changes between 1.8 and 2.0.
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
Test for UNDEF and for NULL on that line. The test for âUninitializedâ does nothing and should be removed.
Great, thank you so much !!! works like a charm
Hello.
I try to use your code also and I have this rule:
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
Do you have any ideea of what I do wrong?
Thankâs a lot!
Change UNDEF to NULLâŠ
Thanks a lot, everything is ok now.
But how do I keep my values in time so I can make charts by day / month / year / etc? What values should I send to influxdb?