No problem. I understand when users get frustrated. But for self preservation, when I see users making statements that I believe are false I have to respond to correct the false parts or else future users will come along, read the false or misleading statement, and believe. I spend an inordinate amount of time correcting false impressions of this sort.
So my target audience was less you and more to the future readers of this thread.
I understand your frustration and believe me when I say we have all been where you are. When we first start out and are learning something we end up making a lot of design and approach mistakes because we don’t know any better. This is often called “technical debt”. My first set of Rules were riddled with technical debt. My sensorReporter script on github is currently full of technical debt almost to the point where I’m considering scrapping it starting over.
As you learn a language and a system your Rules start to become better, clearer, shorter, and more elegant. But what about all the old Rules that you wrote when you didn’t know any better? Well, those Rules are technical debt. Technical debt builds up much like any debt that you accrue without paying off, until it becomes unmanageable. At that point you have to either pay it back or declare bankruptcy (i.e. start over from scratch). It sounds like you’ve a lot of technical debt to pay off.
To provide a concrete example, here is the evolution of my presence detection Rules:
First version, didn’t work very well. It was brittle, often failed, and almost 100 lines of code.
import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import org.openahb.core.persistence.*
import org.openhab.model.script.actions.*
import org.joda.time.*
import org.eclipse.xtext.xbase.lib.*
//-------------------------
// Global Variables
//-------------------------
val String getLocCmd = "{\"_type\":\"cmd\", \"action\":\"reportLocation\"}"
val int flappingMins = 5
//-----------------------------------------------------------------------------
// Return true if either passed in switch is ON
//-----------------------------------------------------------------------------
val Functions$Function2 isHome = [ SwitchItem net, SwitchItem gps |
return (net.state == ON || gps.state == ON)
]
//-----------------------------------------------------------------------------
// Ping the phones at startup
//-----------------------------------------------------------------------------
rule "Presence System Startup"
when
System started
then
sendCommand(S_V_RichLoc, OFF)
sendCommand(S_V_JennLoc, OFF)
end
//-----------------------------------------------------------------------------
// Check every five minutes to see if I or Jenn are home based on the presence
// of our phones. If no phones are detected, wait five minutes and check again
// and only then change Presence to OFF
//-----------------------------------------------------------------------------
rule "Periodically check presence"
when
Time cron "0 */5 * * * ?"
then
var boolean jennHome = isHome.apply(S_V_JennPhone, S_V_JennLoc) as Boolean
var boolean richHome = isHome.apply(S_V_RichPhone, S_V_RichLoc) as Boolean
// TODO There should be a way to simplify the rules using these switches
sendCommand(JennHome, if(jennHome) ON else OFF)
sendCommand(RichHome, if(richHome) ON else OFF)
// Update Presence state
if (jennHome || richHome) {
if(Presence.state == OFF) {
logInfo("PresenceCheck", "Someone arrived home")
sendCommand(Presence, ON)
}
}
else {
// Catch both the case of Presence == ON and == Undefined
if(Presence.state != OFF) {
if (S_V_JennPhone.changedSince(now.minusMinutes(flappingMins)) &&
S_V_JennLoc.changedSince(now.minusMinutes(flappingMins)) &&
S_V_RichPhone.changedSince(now.minusMinutes(flappingMins)) &&
S_V_RichLoc.changedSince(now.minusMinutes(flappingMins))) {
logInfo("PresenceCheck", "Nobody is at home")
sendCommand(Presence, OFF)
// Poll the GPS to see if we can force an update
UpdateJennLoc.postUpdate(getLocCmd)
UpdateRichLoc.postUpdate(getLocCmd)
}
}
}
end
//-----------------------------------------------------------------------------
// Called when one of my or Jenn's cell phones are detected near home
//-----------------------------------------------------------------------------
rule "Near home"
when
Item Mobiles changed or
Item Location changed
then
if(isHome.apply(S_V_RichPhone, S_V_RichLoc) as Boolean) {
logInfo("PresenceCheck", "Rich is home")
}
if(isHome.apply(S_V_JennPhone, S_V_JennLoc) as Boolean) {
logInfo("PresenceCheck", "Jenn is home")
}
if (Presence.state != ON) {
if(Mobiles.members.filter(s | s.state == ON).size > 0 ||
Location.members.filter(s | s.state == ON).size > 0) {
logInfo("PresenceCheck", "Somebody arrived home")
sendCommand(Presence, ON)
}
}
end
I won’t post the full history of refactoring I’ve done on these Rules. But based on the the commit history, I’ve refactored these Rules at least six times over the past three years. The current version is below. It is less than 50% the length of the original, works WAY better, never failing or causing something else to fail, it can be expanded simply by adding new Items and adding those Items to the gPresent Group. And it does pretty much exactly the same thing as the original.
val logName = "presence"
rule "Reset vPresent to OFF on startup"
when
System started
then
vPresent.sendCommand(OFF)
gPresent.sendCommand(OFF)
end
rule "A presence sensor updated"
when
Item gPresent changed
then
logInfo(logName, "gPresent changed to " + gPresent.state)
if(tPresent.state == ON && gPresent.state == vPresent.state) {
logInfo(logName, "Timer is running but group and proxy are the same, cancelling timer")
tPresent.postUpdate(OFF)
}
else if(gPresent.state == vPresent.state) return;
if(gPresent.state == OFF) {
logInfo(logName, "Everyone is away, setting anti-flapping timer")
tPresent.sendCommand(ON)
}
else if(gPresent.state == ON) {
logInfo(logName, "Someone came home, setting presence to ON")
vPresent.sendCommand(ON)
}
end
rule "Present timer expired, no one is home"
when
Item tPresent received command OFF
then
logInfo(logName, "Everyone is still away, setting presence to OFF")
vPresent.sendCommand(OFF)
end
And even now as I look at it, there are somethings I can change to remove a few lines of code and clean it up a little. But I’m willing to carry that technical debt for now.
So my recommendation is to pick something, preferably something relatively short. Then look at Design Pattern: DRY, How Not to Repeat Yourself in Rules DSL and see how you can apply these techniques to start to refactor and improve your Rules.