[SOLVED] Making this rule generic

Hi All,

This rule I have works well, but I want to try and make it generic, if that’s possible. I’m a bit lost on where to start.

Scenario:

I have alot of cameras, which when motion is detected they switch on a proxy motion item. What I would like to do, is rather than write 8 rules, Id like to try and make 1 rule where the

'when' trigger'

is done using a member of the group,

gMotionSensors

Intention is to turn on the lights with a 1minute timer when the camera detected motion, but using the triggerItem from the group. So, that if 4 cameras were all triggered, 4 lights would turn on for 1 minute.

Is this even possible?

My current individual rule:

Group:Switch:OR(ON, OFF)    gMotionSensors            "All Motion Sensors"

rule "Front Yard Camera Motion detection turns ON Front Yard Lights when its dark, with a 1 Minute Inactivity Timer"
when
       Item FrontYardMotion received command ON
then
      if(FrontYardMotion_Armed.state == ON){
            if (vTimeOfDay.state == "EVENING" || vTimeOfDay.state == "NIGHT") {
                if (FrontYard_Timer !== null) {
                        FrontYardSw1.sendCommand("ON")
                        logInfo("FrontYardMotion","Front Yard Motion Timer rescheduled for " + FrontYard_TimeOut + " minutes")
                        FrontYard_Timer.reschedule(now.plusMinutes(FrontYard_TimeOut))
                } else {
                        logInfo("FrontYardMotion", "Front Yard Motion Detected! Turning ON Front Yard Lights")
                        FrontYardSw1.sendCommand("ON")
                        logInfo("FrontYardMotion","Front Yard Timer created with " + FrontYard_TimeOut + " minutes")
                        FrontYard_Timer = createTimer(now.plusMinutes(FrontYard_TimeOut))
                        [ |
                                if (FrontYardMotion.state ==  ON) {
                                        logInfo("FrontYardMotion","Front Yard Motion triggered, but rescheduled again for " + FrontYard_TimeOut + " minutes")
                                        FrontYard_Timer.reschedule(now.plusMinutes(FrontYard_TimeOut))
                                } else {
                                        logInfo("FrontYardMotion", "Front Yard, No Motion Detected! Turning OFF Front Yard Lights")
                                        FrontYardSw1.sendCommand("OFF")
                                        FrontYard_Timer = null
                                }
                        ]
                }
        }
}
end

Thankyou

Trigger on group changes, use a switch or forEach statement to cycle through the group members and compare with triggeringItem.name, then apply individual actions.
Or use instanceof as explained here
Or check out this advanced solution

1 Like

Thanks Markus, glad its possible.

Ill have a good read through the thread to try and work something out

I have a number of generic rules and the secret I have found is to make sure the members have names that follow an easy scheme, that can be exploited across devices.

In your case for example, let’s say you have just 2 cameras and 2 lights. FY1_Light should come on when FY1_Cam detects motion, and likewise FY2_Light should come on if FY2_Cam detects motion.

create a group gMotionSensors and add FY1_Cam and FY2_Cam.

Group gMotionSensors            "Frontyard Motion Sensors"

and alsoe one for the Front yard Lights

Group gfrontYardLights "Frontyard Lights"

I think the rule could then be simply. (its where I would start from)

rule "This needs work"
when
	Member of  gMotionSensors received command ON
then
	logInfo("frontyard_motion.rules", triggeringItem.name + " detected motion.")
    val source = triggeringItem.name.split("_").get(0)
    val _Cam = gMotionSensors.members.findFirst[ cam | cam.name == source"_Cam" ]
    val _Light = gfrontYardLights.members.findFirst[ light | light.name == source"_Light" ]
	sendCommand(_Light,ON)
	createTimer(now.plusSeconds(60)) [ | {sendCommand(_Light,OFF)}]
end

Not sure if this will run as is or if it needs lots of work, worth a shot though.

You essential take most of the work out of the rule by a well constructed item naming scheme.
Don’t forget to add in your IF conditions that you want such as armed and time-of-day.

Regards

Paul

2 Likes

Hi Paul

I have the following group:


/* Group Definitions for Motion Sensors*/
Group:Switch:OR(ON, OFF)    gMotionSensors            "All Motion Sensors"

All my cameras have

_Motion

And my light switches are

_Sw1 or _Sw2

on the end of the name. Yes, I will use time of day and also armed, because I have proxies for each one to arm/disarm. How can I check the arming state as a generic item? Rather than each individual camera.

What, like the if (timeofday) in the rule that you started with? Same.

I can do that with TimeofDay, but what about armed? Because I have 8 proxies for the cameras, one for each camera. Im not sure how I’d check if the triggeringItem is also armed. Does that make sense?


/* Motion Sensor Item for Blue Iris Cameras */

Switch NorthWestDriveMotion                    "[MAP(motion.map):%s] North West Drive Camera" (gMotionSensors)
Switch PatioMotion                             "[MAP(motion.map):%s] Patio Camera"            (gMotionSensors)
Switch PatioBBQMotion                          "[MAP(motion.map):%s] Patio BBQ Camera"        (gMotionSensors)
Switch EntranceWestMotion                      "[MAP(motion.map):%s] Entrance West Camera"    (gMotionSensors)
Switch LibraryMotion                           "[MAP(motion.map):%s] Library Camera"          (gMotionSensors)
Switch Bedroom1Motion                          "[MAP(motion.map):%s] Bedroom 1 Camera"        (gMotionSensors)
Switch DrivewayNorthMotion                     "[MAP(motion.map):%s] Driveway North Camera"   (gMotionSensors)
Switch EastMotion                              "[MAP(motion.map):%s] East Camera"             (gMotionSensors)

/* Enables/Disables Cameras as Motion Sensors */

Switch NorthWestDriveMotion_Armed                   "[MAP(motion.map):%s] North West Drive Camera" (gMotionSensors)
Switch PatioMotion_Armed                            "[MAP(motion.map):%s] Patio Camera"            (gMotionSensors)
Switch PatioBBQMotion_Armed                         "[MAP(motion.map):%s] Patio BBQ Camera"        (gMotionSensors)
Switch EntranceWestMotion_Armed                     "[MAP(motion.map):%s] Entrance West Camera"    (gMotionSensors)
Switch LibraryMotion_Armed                          "[MAP(motion.map):%s] Library Camera"          (gMotionSensors)
Switch Bedroom1Motion_Armed                         "[MAP(motion.map):%s] Bedroom 1 Camera"        (gMotionSensors)
Switch DrivewayNorthMotion_Armed                    "[MAP(motion.map):%s] Driveway North Camera"   (gMotionSensors)
Switch EastMotion_Armed                             "[MAP(motion.map):%s] East Camera"             (gMotionSensors)



Group:Switch:OR(ON, OFF)    gMotionSensors            "All Motion Sensors"

/*Group Definitions for Lights*/
Group:Switch:SUM gAllLights          "All Lights"

/*ZWave Switch Node 2 Bedroom Lights*/
Switch BedRoom1_Sw1  "Bedroom West Exterior"                                      (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node2:switch_binary1" }
Switch BedRoom1_Sw2  "Bedroom East Exterior"                                      (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node2:switch_binary2" }

/*ZWave Switch Node 3 Laundry Lights*/
Switch Laundry_Sw1  "Laundry Entrance Exterior"                                   (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node3:switch_binary1" }
Switch Laundry_Sw2  "Laundry Driveway Exterior"                                   (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node3:switch_binary2" }

/*ZWave Switch Node 4 Hall Lights*/
Switch Hall_Sw1  "Front Door"                                                     (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node4:switch_binary1" }
Switch Hall_Sw2  "West Garden"                                                    (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node4:switch_binary2" }

/*ZWave Switch Node 5 Library West Lights*/
Switch Library_Sw1  "Library West Exit"                                           (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node5:switch_binary" }

/*ZWave Switch Node 6 Wall Pendant Lights*/
Switch Kitchen_Sw1   "Wall Pendant"                                               (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node6:switch_binary" }

/*ZWave Switch Node 8 Bird Lights*/
Switch Bird_Sw1  "Bird Light"                                                     (gLights_Random,gAllLights,gInsideLights)          { channel="zwave:device:ce741a7c:node8:switch_binary" }

/*ZWave Switch Node 10 Patio/Eastern Exterior Lights*/
Switch Patio_Sw1             "North Patio Lights"                                 (gLights_Random,gAllLights,gOutsideLights)         { channel="zwave:device:ce741a7c:node10:switch_binary1" }
Switch EasternExteriorSw2   "Eastern Exterior Light"                             (gLights_Random,gAllLights,gOutsideLights)         { channel="zwave:device:ce741a7c:node10:switch_binary2" }

/*ZWave Switch Node 14 Western Exterior Light*/
Switch Node14_Sw1   "West Exterior Light"                                         (gLights_Random,gAllLights,gOutsideLights)         { channel="zwave:device:ce741a7c:node14:switch_binary" }


rule "Motion activated Lights using CCTV Cameras"
when
	Member of gMotionSensors received command ON
then
	logInfo("Motion.rules", triggeringItem.name + " detected motion.")
     if (vTimeOfDay.state == "EVENING" || vTimeOfDay.state == "NIGHT") {
      val source = triggeringItem.name.split("_").get(0)
      val _Cam = gMotionSensors.members.findFirst[ cam | cam.name == source"_Cam" ]
      val _Light = gAllLights.members.findFirst[ light | light.name == source"_Sw" ]
	sendCommand(_Light,ON)
	createTimer(now.plusSeconds(60)) [ | {sendCommand(_Light,OFF)}]
     }
end


I don’t really understand. Getting name Patio_armed from name Patio is pretty straightforward and exampled in Paul’s rule. (Albeit it needs source + “_extra” syntax fixing)

Sure, but is it possible using an if statement to check if the triggeringItem (which was a Motion sensor) has its corrosponding ‘Armed’ item as ON?

Does that make sense?

If Camera tripped motion, and its corresponding Armed Sensor is ON, then turn on light Blah.

Yes of course. Have you tried? Maybe do it in steps, all the parts are already shown in this thread. Trigger a rule. Get the triggering Item name. Use that to build the armed item name. Use that to fetch the Item from a group. Now you can construct an if() evaluation of that Item’s state. Then you can turn the light on (or not).

I can’t try because I dont have access to the system as yet. OK, so I can construct the Armed item from the triggeringItem as the names are the same, the Armed just has _Armed prepended to the end.

rule "Motion activated Lights using CCTV Cameras"
when
	Member of gMotionSensors received command ON
then
	logInfo("Motion.rules", triggeringItem.name + " detected motion.")
     if (vTimeOfDay.state == "EVENING" || vTimeOfDay.state == "NIGHT") {
      val source = triggeringItem.name
      val armed = triggeringItem.name+"_Armed" as SwitchItem
      }
     if (armed.state == ON){

Does that look ok? I will need to change the Light names to match the triggeringItem to some degree I would guess. I don’t really understand the logic in Paul’s example with respect to:

 val _Cam = gMotionSensors.members.findFirst[ cam | cam.name == source"_Cam" ]
      val _Light = gAllLights.members.findFirst[ light | light.name == source"_Sw" ]

Nope. armed in your rule is just a string, the name of the Item you want. It doesn’t have a .state, it’s just a string not an Item.

There are at least two ways to get hold of the actual Item with that name (so that you can in turn get hold of its state),
You can select it from a group of which it is a member, as already shown in in this thread, or you can use a registry action.

Learning a language is incredibly difficult for someone without programming background so apologies for what may seem extremely trivial to you.

import org.eclipse.smarthome.model.script.ScriptServiceUtil
val _Armed = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"_Armed") as SwitchItem

To end up with:

import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "Motion activated Lights using CCTV Cameras"
when
	Member of gMotionSensors received command ON
then
	logInfo("Motion.rules", triggeringItem.name + " detected motion.")
     if (vTimeOfDay.state == "EVENING" || vTimeOfDay.state == "NIGHT") {
      val source = triggeringItem.name
      val _Armed = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"_Armed") as SwitchItem
      }
     if (_Armed.state == ON){

I can only speak from my own experience and I use the name to make things easy, have two lights one called _Sw1 and one called _Sw2 does not really help when trying to make things generic.

I am sure some genius around here can assist but its beyond my abilities, whereas the way I described is how I do all of my generic rules pretty much.

Take the easy way or continue down the harder road.

I am sorry I could not help

Thanks

Paul

1 Like

You’re helping Paul. Don’t be sorry. Yes I think I’ll have to rename my lights to match my motion sensors and camera names with underscores to allow the user of triggeritem.name

I’m trying to understand what your syntax means :slight_smile:

The general syntax I use is

  1. the delimiter is an underscore, this is useful and supper easy to locate when breaking the name into different parts inside rules.
  2. The first part is an abbreviation for the location, e.g LR - Livingrroom, BR1 equals Bedroom 1 and KT equals Kitchen.
  3. The second part equals the device type.
    4.The final part is the specific cahannel so if its simply the device on /off you could use ‘Switch’ or for such a default actual just omit it. I went with omitting it but if I started again I would give using ‘Switch’ a go. other obvious actions are Online, LastSeen and Brightness.

putting that all together ends with names like this

KT_PIR_Detect
so Kitchen PIR sensor Detect

Or BR1_Fan_Switch
Bedroom 1 fan switch
Hope the naming syntax makes sense.

now to grab a part of a triggering name item which is part of a group you simply do this

val location = triggeringItem.name.split("").get(0)
val device= triggeringItem.name.split("
").get(1)
val action= triggeringItem.name.split("_").get(3)

The three values now contain the individual parts for the triggered item.
These parts can be used to make up other item names so say you have a motion detector trigger in the Library and you want the library light to come on.

if (__Cam==ON) {
     sendConnand(_Light,ON)
}

This assumes that you have made _Cam match the Motion Detector of the triggering Camera, and the _Light is trhe deterrent light you want turned on in this event.

I really think you need to start slowly and get access to a system before you try such an advanced topic, with a little playing you will figure it out.

Regards

Paul

1 Like

Thanks Paul, what is the significance of the .get(0) to get(3)?

its returning the part based on the Underscore delimiter of the item name.

Call me stupid but that makes no sense

If your triggeringItem name is: KT_PIR_Detect

val location = triggeringItem.name.split("").get(0) (So, this returns KT?)
val device= triggeringItem.name.split(" “).get(1) (So, this returns PIR?)
val action= triggeringItem.name.split(”_").get(3) (So, this returns Detect?)

Why have the underscore the the last one but not the first ones. I don’t see any consistency.

I think that’s just what the forum has done to underscores outside of code fences.
Like the way it will hide <angles> if you don’t fence them.
The third part should be get(2) as well.

But you’ve grasped the idea of the split / get tools, it’s just string manipulation.