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
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
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.
/* 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.
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?
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)
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.
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
the delimiter is an underscore, this is useful and supper easy to locate when breaking the name into different parts inside rules.
The first part is an abbreviation for the location, e.g LR - Livingrroom, BR1 equals Bedroom 1 and KT equals Kitchen.
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.
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.