Help shortening one of my persistence rules

Hi everyone can someone shorten this rule for me i like too learn new rule stuff this helps as alot of it is the same i’m sure it can be shortened

rule "Kitchen Motion Last Updated"
when
    Item Kitchen_Motion changed
then
    KitchenMotionLastTriggered.postUpdate(Kitchen_Motion.lastUpdate.toString)
end

rule "Back Door Motion Last Updated"
when
    Item Back_Door_Motion changed
then
    BackDoorMotionLastTriggered.postUpdate(Back_Door_Motion.lastUpdate.toString)
end


rule "LR Motion Last Updated"
when
    Item Living_Room_Motion changed
then
    LrMotionLastTriggered.postUpdate(Living_Room_Motion.lastUpdate.toString)
end

rule "Front Door Motion Last Updated"
when
    Item Frontdoor_Motion changed
then
    FrontDoorMotionLastTriggered.postUpdate(Frontdoor_Motion.lastUpdate.toString)
end

rule "Landing Motion Last Updated"
when
    Item Landing_Motion changed
then
    LandingMotionLastTriggered.postUpdate(Landing_Motion.lastUpdate.toString)
end

rule "Toilet Motion Last Updated"
when
    Item Toilet_Motion changed
then
    ToiletMotionLastTriggered.postUpdate(Toilet_Motion.lastUpdate.toString)
end

rule "Bathroom Motion Last Updated"
when
    Item Bathroom_Motion changed
then
    BathroomMotionLastTriggered.postUpdate(Bathroom_Motion.lastUpdate.toString)
end

rule "Master Bed Motion Last Updated"
when
    Item MBed_Motion changed
then
    MbedMotionLastTriggered.postUpdate(MBed_Motion.lastUpdate.toString)
end

Put them all in a group ‘gMotion’
You will have to change the name of a couple of items for consistency

Back_Door_Motion into BackDoor_Motion
Frontdoor_Motion into FrontDoor_Motion
Living_Room_Motion into LivingRoom_Motion
LrMotionLastTriggered into LivingRoomMotionLastTriggered
rule "motion times"
when
    Member of gMotion changed
then
    var String name = triggeringItem.name.toString.split("_").get(0)
    postUpdate(name + "MotionLastTriggered", triggeringItem.lastUpdate.toString)
end
1 Like

Hi again Vincent

They already are and i already knew too use member of gmotion changed but didnt have a clue after that :wink:

that’s alot shorter than my version :slight_smile: but i haven’t got a clue how that works its complex through simplicity

i haven’t learned how to use variables yet

I think i can read some tiny parts i could not make it myself

OK

var String name creates a string variable (A chain of characters) like “hello” or “12345”
triggeringItem returns the item that triggered the rule let’s say BackDoor_Motion
.name returns the name of that item
.toString turns that into a String “BackDoor_Motion”
.split("_") returns 2 strings "BackDoor" and "Motion" (It's the string "BackDoor_Motion" split in 2 at the "_" characterget(0)` returns the first of those two strings “BackDoor”

So now we have a variable containing “BackDoor”

Then we use the postUpdate action which takes strings as arguments:
postUpdate(item String, value String)

So the item is `name + “MotionLastTriggered”, in our case “BackDoor” + “MotionLastTriggered” gives one longer string “BackDoorMotionLastTriggered”
This is called concatenating string (Adding them up)

And the value for the postUpdate is the same as you used in your rules except that we use the triggeringItem implicit variable in the rule.
In our case BackDoor_Motion

Does that make sense?

We use the name of the triggering item to generate the name of the item to be updated. That’s why you need to rename some items so that the rule will work consistently.

1 Like

Not completely no but yes some of it i understood

Thanks for that response i like replys like that its food for thought

90% understood :slight_smile: i can sort of see how it would work but i could not use what i have learned too edit my other rules myself i could shorten alot of my rules using this too :frowning:

What did you not understand and I will try to explain it better?

I’m not sure tbh i do actually sort of understand how that works, i think its just because i have not dealt with this sort of stuff before its the normal OH Jitters i felt the same when i didn’t know how to use text files its overwhelming too look at but simple when you have used it a few times

i just don’t know how too build that sort of rule myself from scratch but i will read it more look at my rules find one similar and try too do the same myself will post my rule and result

yours is a work of art compared too my long winded version and i would like too learn how too do that myself

where can i learn more about this ?

can you do the same for this rule please so i can see what you did again

rule "Motion Detection (PART)"
when
    Item Kitchen_Motion changed from OFF to ON or //Kitchen Sensor
    Item Frontdoor_Motion changed from OFF to ON or  //Front Door Sensor
    Item Back_Door_Motion changed from OFF to ON or   //Back Door Sensor
    Item Living_Room_Motion changed from OFF to ON   //Living Room Sensor
then
    if(Security_System_PART.state ==ON) {
        if(Kitchen_Motion.state ==ON ) {
            sendNotification("myemail@live.com", "Kitchen Motion Detected!")    
        }
        if(Frontdoor_Motion.state ==ON ) {
            sendNotification("myemail@live.com", "Front Door Motion Detected!")    
        }
        if(Back_Door_Motion.state ==ON ) {
            sendNotification("myemail@live.com", "Back Door Motion Detected!")    
        }
        if(Living_Room_Motion.state ==ON ) {
            sendNotification("myemail@live.com", "Living Room Motion Detected!")    
        }
    }
end

The Design Pattern postings. The DP Vincent is illustrating is Design Pattern: Associated Items.

Assuming you put the motion sensors in a Group, let’s call if MotionSensors…

rule "Motion Detection (PART)"
when
    Member of MotionSensors changed from OFF to ON
then
    if(Security_System_PART.state != ON) return; // fail fast, we do nothing if it isn't ON so just exit the Rule

    val message = triggeringItem.name.replace("_", " ") + " Detected!" // replace the underscores with spaces in the Item name an append the word "Detected!"  as the message.

    sendNotification("myemail@live.com", message) // send the message
end

This code uses Design Pattern: How to Structure a Rule.

  1. Determine if the Rule needs to run at all, and if not exit.
  2. Calculate what needs to be done. In this case create the message to send in the notification.
  3. Do the action, in this case send the notification.
1 Like

@rlkoshak

As you can see I still have quite alot too learn my rules are just full of if statements at the moment I do realy like the way you have both rewritten them rules some took quite awhile for me too write but yours ways would take seconds it’s just getting my head fully around how all that works I can see it with if statements I suppose its like iv already said its new and practice makes perfect it wasent long ago I didn’t understand the text rules and items ect but now I do

I’m going too have a read of the design patterns i think it’s time

It comes with practice and experience. It comes easy for me because I’ve coded professionally for decades and my experience on this forum helping people with Rules has given me a whole lot of experience and practice.

The key thing is when you find yourself typing the same line(s) of code over and over with only minor changes that is considered a violation of the DRY principal. Don’t Repeat Yourself.

I looked at your example above and I see that you are repeating yourself with the Rule triggers -> Move to Member of trigger.

I looked at your Rule body and saw essentially the same if statement and sendNotification line over and over. So my coder spidey sense goes off and I ask, how can I keep from repeating these lines over and over. Well, the first thing I can do is only call sendNotification once in the Rule at the bottom. That clears out that repeated line. So what is different in the repeated calls to sendNotification? It’s the message so before I get to the Rule I need to calculate the message to send.

So far so good. The Rule would look something like this:

rule "Motion Detection (PART)"
when
    Member of MotionSensors changed from OFF to ON
then
    // blah blah bla
    
    val message = ???

    sendNotification("myemail@live.com", message)
end

So now I need to focus on how to calculate the message. I look for patterns and I see the the message is just the Item name using spaces instead of underscores with " Detected!" appended to it. That makes it easy. I just need to do some work with triggeringItem’s name and I can create the message in one line.

Often this isn’t the case and this middle part of the Rule would be a bit more complicated.

So the Rule becomes:

rule "Motion Detection (PART)"
when
    Member of MotionSensors changed from OFF to ON
then
    // blah blah bla
    
    val message = triggeringItem.name.replace("_", " ") + " Detected!"

    sendNotification("myemail@live.com", message)
end

Phew, no more duplicated code. Now we need to look at the rest of the Rule. All that is left is to check if Security_System_PART.state is ON and not run the Rule if it is not. I could put the rest of the Rule inside an if statement, but I find Rules with fewer indents and curly brackets are simpler and easier to follow, so we will flip the if around and exit the Rule if that Item is anything but ON.

And we end up with the Rule I posted above.

I can’t speak for Vincent’s thought processes, but here is how I would go about thinking about your OP Rules.

I see basically the same Rule repeated over and over doing the same thing on different Items. My DRY spidey sense is going off. Let’s look for the patterns in what is different between the Rules.

  • Rules are all triggered by a Motion sensor Item.
  • Each Rule has an associated Item. I.e. each motion sensor has an associated LastTriggered Item and the names are almost such that I can take the name of the triggeringItem and append “LastTriggered” to it and will have the name of the associated Item.
  • When the Motion sensor changes we update the associated Item with it’s last update.

Something else comes to mind which is a result of experience and I wouldn’t expect most users to think of this at first. The Rules are going back to persistence to get the lastUpdate value that is being sent to the LastTriggered Item. Unfortunately, persistence may not be done saving the most recent value to the database when this Rule runs so you can’t really rely on lastUpdate without a small sleep (100-200 msec) is usually enough. But this Rule gets triggered from that most recent update anyway so we can avoid the call to Persistence and the risk that we get the wrong value and instead just use now.

Therefore the recommendation would be:

  • Merge them all into one Rule.
  • Use a Group and Member of to trigger this merged Rule
  • Rename the LastTriggered Items so the first part matches the Motion sensor Item names
  • postUpdate using now

Resulting in:

rule "Kitchen Motion last Updated"
when
    Member of Member of MotionSensors changed
then
    postUpdate(triggeringItem.name+"LastTriggered", now.toString)
end

Now I notice that you have two Rules that trigger when those MotionSensors go off. They could be merged into one Rule.

rule "Motion Detection (PART)"
when
    Member of MotionSensors changed
then
    postUpdate(triggeringItem.name+"LastTriggered", now.toString)

    if(Security_System_PART.state != ON || previousState != OFF || triggeringItem.state != ON) return; 

    val message = triggeringItem.name.replace("_", " ") + " Detected!" // replace the underscores with spaces in the Item name an append the word "Detected!"  as the message.

    sendNotification("myemail@live.com", message) // send the message
end

So what changed? I removed the from part of the Rule trigger. I want this Rule to trigger on any change.

I added a new line to update the LastTriggered Item with now.

Then I added some more checks to replace the from OFF to ON part that I removed from the Rule trigger so the notification will only be sent in the case where the sensor went from OFF to ON.

That is how I think about code like you posted and come up with how to improve it. That is how I (and probably others like Vincent too) can take 83 lines of code scattered across 9 Rules and collapse them down to a single Rule of 12 lines of code. You too will get to this point and probably faster than you realize. None of us, even the professional coders, start out writing these super concise and efficient Rules. It took me well over a year of helping people on this forum and applying little tricks that I’ve learned to halve the number of lines in my Rule, then halve them again.

And when I discover some new problem I’ve not encountered before or some new way to improve the code, I write a DP to document it. That is why often a response to questions like this will sometimes just be a link to a DP. I and the others who have written DPs try to do so with lots and lots of description and documentation, much more than could be included in a response to a question.

Over time you will start to think about Rules in this way more and more. You have already taken the first step. Your spidey sense is going off. Otherwise you wouldn’t have posted here in the first place.

2 Likes

Yes i dont like too do that its time consuming too write rules ect so when you are just rewriting the same thing its pretty annoying

I have slowely started too add this i just wasen’t sure how it works
and some of the items are not in subgroups like the motion sensors only in 1 group Gmotion so where my security system is in mode part member of Gmotion would not work here i need too add some sub groups :slight_smile: groups are a pretty new addition too my setup too

HAHA :slight_smile:

I didn’t notice that myself lol

i wonder if i posted my entire system how small it could be shrank too the way i have it running is not efficient

Mine were even worse than this a few months ago when i knew less i had a single rule for pretty much everything every change too my light had its own rule i managed too sort that when i learnead about the if statements now my rules are shorter but filled with if statements

openhab is my first expericnce using code i have changed .ini files when i used too use the program rainmeter

Thanks for the extremely detailed response i appreciate that and i will start looking at the design patterns more too improve my rules

And take away this opportunity to apply what you’ve learned here and to learn some more? Never! :smiley:

I try not too post my problems for others too solve i post for help on how too do it myself and too understand better i know a few regulars already know that

I just sometimes ask for things that are currently beyond my ability like scripting

its all a learning process ,

I hear people moaning saying OH has got a super steep learning cure but to be honest i love OH for that

When i finally learn how too create them work of art rules in 5 years :wink: i will let you know how small my system is

how big are your rules atm ?

[19:34:24] openhabian@openHABianPi:~$ wc -l /etc/openhab2/rules/*.rules
   48 /etc/openhab2/rules/Amazon Dash.rules
   66 /etc/openhab2/rules/Climate Control.rules
  100 /etc/openhab2/rules/Kids.rules
  258 /etc/openhab2/rules/Logic.rules
  279 /etc/openhab2/rules/LR Lighting.rules
  280 /etc/openhab2/rules/Main Routines.rules
  101 /etc/openhab2/rules/New.rules
   45 /etc/openhab2/rules/Notifications.rules
   82 /etc/openhab2/rules/Presence Detection.rules
  325 /etc/openhab2/rules/Security System.rules
  127 /etc/openhab2/rules/Unused.rules
 1711 total

Alot of them are in the same state with code repeating

It has been awhile since I’ve checked. To a large extent, the number of lines of code isn’t really all that good of a measure of code. But it is an easy metric to collect so everyone seems to use it.

Anyway

rich@argus:/o/o/c/rules (master) ✗   wc -l *.rules
   93 admin.rules
   38 alarm.rules
   15 christmas.rules
   53 climate.rules
   42 dad.rules
  183 dexcom.rules
   98 entry.rules
   71 lights.rules
   11 media.rules
   33 other.rules
  115 presence.rules
   20 utilities.rules
  130 weather.rules
  902 total

Note that the dexcom.rules are non-functioning. Well, they function but dexcom delays the data by three hours making it worthless for home automation. And the Christmas Rules are kind of adhoc. I take a bunch of my controllable outlets and reassign them to Christmas lights so these rules are inactive most of the year.

So my entire home automation is running on around 700 lines of active code.

One thing you will notice is that almost all of these just about fit on a single screen. I like that. :slight_smile: The only long file is the dexcom one, and that is because it would properly be implemented in a binding anyway and once I figured out the data is useless I never went back to edit the code down. As with writing, the first draft of a piece of code is rarely any good.

But you can see that they are alot smaller than mine I have 1000 lines more than you and I bet your setup does more than mine

Mine do not fit on the screen :smiley: