I’m partway through the journey - I have it working as I wish it to work using the legacy rules DSL. I’m still considering stepping into the new language, and haven’t decided which language would suit me most. It needs to wait until I’m less busy at work.
In case anyone is having similar issues or otherwise comes to this thread looking for help, I’m posting the full rules file that is now working quite well. Key areas of change were:
- declare most of my procedures as elements within a hash. This is ugly, but it means that I don’t have to keep passing all the procedures into every procedure call - the rules DSL doesn’t deal with globally declared procedures well and if you want one procedure to call another procedure (lambda really I guess) you have to explicitly pass it in. I still kept a couple outside the hash, because the downside of the hash is calling them directly is ugly - and my event based rules call into these directly - so a couple are outside the hash, a few inside the hash, and some kind of live in both places.
- similarly, declaring my various config data all within one big hash called “StaticData”, which also holds those procedures above. This avoids passing lots of parameters around
- removing a lot of types from things. Visual Studio Code (actually the DSL I think) complains that things are untyped, so in the past I’d put types on them. But you can just ignore those warnings, and it’s less likely to tie itself in knots and declare your syntax to be invalid. This means casting many things when you need to use them
- generally cleaning up the code to generalise some concepts better. In particular I didn’t want to turn outside lights on, then walk past a motion sensor, and have the motion sensor turn that light off (I turned it on for a reason). So I wrote a concept of triggers - a light can be turned on by multiple events, it doesn’t turn off until all those triggers clear. But, I do want to turn lights off by a switch different than the one that turned it on…so the trigger concept applies to all timers and motion sensors, but when I turn a light off with a switch I want it to go off now, not wait for the timers
- other than syntax, the main thing I cannot easily do is generalise the channel calls - the Shellys trigger on a channel for the button presses rather than being a switch event or similar. That means you can’t use a group to handle them as a job lot. There are ways, but in the end it was easier to just enumerate them as a list of rules than it was to mess with that. Somewhere I’d have had to enumerate them anyway, so I’d really just be shifting that from rules to items. It feels better, but it’s not really less work
All good fun, and other than usual syntax messing around, it works pretty cleanly now. The Shellys are a bit more fussy than I might have expected, losing their config and needing restarting. They seem to have settled down now that I’m not changing them, so we’ll see if they’re stable across events like power cuts.
import java.util.HashMap
import java.util.List
// Static Config
val HashMap StaticData = newHashMap(
"SinglePress" -> newHashMap(
"SensorDaybed" -> (newArrayList ("SensorDaybed", "SensorDining")),
"SensorStudy" -> (newArrayList ("SensorStudy", "SensorLaundry", "SensorBoot")),
"SensorLaundry" -> (newArrayList ("SensorLaundry", "SensorStudy", "SensorBoot")),
"SensorAtrium" -> (newArrayList ("SensorAtrium")),
"SensorBoot" -> (newArrayList ("SensorBoot", "SensorKitchen", "SensorGarage", "SensorDining")),
"SensorGarage" -> (newArrayList ("SensorGarage", "SensorKitchen", "SensorBoot", "SensorDining")),
"SensorKitchen" -> (newArrayList ("SensorKitchen", "SensorGarage", "SensorDining")),
"SensorDining" -> (newArrayList ("SensorDining", "SensorKitchen", "SensorGarage", "SensorDaybed")),
"FloodLounge" -> (newArrayList ("FloodLounge")),
"FloodMaster" -> (newArrayList ("FloodMaster")),
"FloodTractor" -> (newArrayList ("FloodTractor")),
"LightGarage" -> (newArrayList ("LightGarage")),
"LightBootDoor" -> (newArrayList ("LightBootDoor", "LightBootHall")),
"LightBootHall" -> (newArrayList ("LightBootHall", "LightBootDoor")),
"Triple" -> (newArrayList ("SensorDaybed", "SensorDining", "SensorStudy", "SensorAtrium", "SensorLaundry", "SensorBoot", "SensorGarage", "SensorKitchen", "FloodLounge", "FloodMaster", "FloodTractor")),
"AllOff" -> (newArrayList ("SensorDaybed", "SensorDining", "SensorStudy", "SensorAtrium", "SensorLaundry", "SensorBoot", "SensorGarage", "SensorKitchen", "FloodLounge", "FloodMaster", "FloodTractor", "LightBootHall", "LightBootDoor"))
),
"DoublePress" -> newHashMap(
"SensorDaybed" -> (newArrayList ("SensorDaybed", "SensorDining", "FloodLounge", "FloodMaster", "FloodTractor")),
"SensorStudy" -> (newArrayList ("SensorStudy", "SensorLaundry", "SensorBoot", "SensorDaybed", "SensorDining", "FloodLounge", "FloodMaster", "FloodTractor")),
"SensorLaundry" -> (newArrayList ("SensorLaundry", "SensorStudy", "SensorBoot", "SensorAtrium")),
"SensorAtrium" -> (newArrayList ("SensorAtrium", "SensorStudy", "SensorLaundry", "SensorBoot")),
"SensorBoot" -> (newArrayList ("SensorBoot", "SensorStudy", "SensorLaundry", "SensorKitchen", "SensorGarage", "SensorDining", "FloodLounge", "FloodMaster", "FloodTractor")),
"SensorGarage" -> (newArrayList ("SensorGarage", "SensorKitchen", "SensorDining", "SensorBoot", "LightBootDoor")),
"SensorKitchen" -> (newArrayList ("SensorKitchen", "SensorGarage", "SensorDining", "FloodLounge")),
"SensorDining" -> (newArrayList ("SensorKitchen", "SensorGarage", "SensorDining", "SensorDaybed", "FloodLounge", "FloodMaster", "FloodTractor")),
"FloodLounge" -> (newArrayList ("FloodLounge", "FloodMaster", "FloodTractor", "SensorDining", "SensorDaybed")),
"FloodMaster" -> (newArrayList ("SensorDaybed", "SensorDining", "SensorStudy", "SensorLaundry", "SensorAtrium", "SensorBoot", "SensorGarage", "SensorKitchen", "FloodLounge", "FloodMaster", "FloodTractor")),
"FloodTractor" -> (newArrayList ("FloodTractor", "FloodMaster", "FloodLounge", "SensorDining", "SensorDaybed")),
"LightGarage" -> (newArrayList ("LightGarage", "SensorKitchen", "SensorGarage", "SensorBoot", "LightBootDoor")),
"LightBootDoor" -> (newArrayList ("LightBootDoor", "LightBootHall", "SensorBoot", "SensorKitchen", "SensorGarage")),
"LightBootHall" -> (newArrayList ("LightBootHall", "LightBootDoor"))
),
"LightTime" -> newHashMap(
"SensorDaybed" -> 10,
"SensorStudy" -> 10,
"SensorLaundry" -> 10,
"SensorAtrium" -> 5,
"SensorBoot" -> 5,
"SensorGarage" -> 10,
"SensorKitchen" -> 10,
"SensorDining" -> 5,
"FloodLounge" -> 15,
"FloodMaster" -> 5,
"FloodTractor" -> 5,
"LightGarage" -> 5,
"LightBootDoor" -> 10,
"LightBootHall" -> 10,
"Triple" -> 10
),
"LightTimers" -> newHashMap(),
"LightTriggers" -> newHashMap(
"SensorDaybed" -> (newHashMap ()),
"SensorStudy" -> (newHashMap ()),
"SensorLaundry" -> (newHashMap ()),
"SensorAtrium" -> (newHashMap ()),
"SensorBoot" -> (newHashMap ()),
"SensorGarage" -> (newHashMap ()),
"SensorKitchen" -> (newHashMap ()),
"SensorDining" -> (newHashMap ()),
"FloodLounge" -> (newHashMap ()),
"FloodMaster" -> (newHashMap ()),
"FloodTractor" -> (newHashMap ()),
"LightGarage" -> (newHashMap ()),
"LightBootDoor" -> (newHashMap ()),
"LightBootHall" -> (newHashMap ())
),
"MotionLights" -> newHashMap(
"MotionBoot" -> (newArrayList("SensorBoot", "SensorGarage", "SensorKitchen", "LightBootDoor", "SensorLaundry", "SensorStudy")),
"MotionKitchen" -> (newArrayList("SensorKitchen", "SensorDining", "SensorGarage")),
"MotionDining" -> (newArrayList("SensorDining", "SensorDaybed", "SensorKitchen")),
"MotionStudy" -> (newArrayList("SensorStudy", "SensorLaundry", "SensorBoot", "SensorDaybed")),
"MotionDaybed" -> (newArrayList("SensorDaybed", "SensorDining"))
),
/*
Logic flow for button presses
Light already off:
Short - add this button to the triggers for each of the short lights, set a timer for the specified length to turn them off again
Double - add this button to the triggers for each of the long lights, set a timer for the specified length to turn them off again
Long - add this button to the triggers for each of the short lights, no timer
Triple - add this button to the triggers for all outside lights, set a timer
Light already on:
Short - remove triggers from all lights for this button, cancel timer, then recalc lights
Double - add this button to the triggers for each of the long lights, reset the timer
Long - cancel timer, leave lights on
Triple - turn all outside lights off irrespective of how they were turned on, clear timer
End of timer:
Turn off all lights that were turned on by this switch (based on the LightTriggers list)
Timers need to be tied to the button that initiated them, so we can cancel them again. That implies a
hashmap that holds them all.
*/
/*
Set a light timer to turn lights off based on the configured timer length for this light switch.
If there is a pre-existing timer, cancel it first
*/
"SetLightTimer" -> [
switchName,
StaticData |
logDebug( 'lights', 'Setting a timer for ' + switchName)
val org.eclipse.xtext.xbase.lib.Procedures$Procedure2 LightTimerEnd = (StaticData as HashMap).get("LightTimerEnd")
val LightTimers = (StaticData as HashMap).get("LightTimers") as HashMap<String, Timer>
val LightTime = (StaticData as HashMap).get("LightTime") as HashMap<String, Long>
if( LightTimers.get(switchName) !== null ){
LightTimers.get(switchName).cancel
LightTimers.put(switchName, null)
}
LightTimers.put( switchName, createTimer(new DateTimeType(ZonedDateTime.now()).getZonedDateTime().plusSeconds(LightTime.get(switchName) * 60)) [|{
// at end of timer, turn off all lights that may have been turned on
logDebug( 'lights', 'Timer turning off lights for ' + switchName)
LightTimerEnd.apply( switchName, StaticData )
}])
],
/*
When a light timer ends, remove this trigger from the LightTriggers for each light, then
recalc light state for each light (i.e. turn off the lights we turned on).
Note that light triggers can be motion as well as switches, hence sourceName
*/
"LightTimerEnd" -> [
sourceName,
StaticData |
logDebug( 'lights', 'Processing timer end for ' + sourceName )
val org.eclipse.xtext.xbase.lib.Procedures$Procedure1 RecalcLights = (StaticData as HashMap).get("RecalcLights")
val LightTriggers = (StaticData as HashMap).get("LightTriggers") as HashMap<String, HashMap<String, String>>
val LightTimers = (StaticData as HashMap).get("LightTimers") as HashMap<String, Timer>
LightTriggers.keySet.forEach[ triggerKey |
LightTriggers.get(triggerKey).remove(sourceName)
]
if( LightTimers.get(sourceName) !== null ){
LightTimers.get(sourceName).cancel()
LightTimers.put(sourceName, null)
}
RecalcLights.apply( StaticData )
if(sourceName == "Triple"){
swLightTriplePress.postUpdate(OFF)
}
if(sourceName == "FloodLounge"){
swFloodLights.postUpdate(OFF)
}
],
/*
When a switch is used to turn off then we actually turn off every light
this switch controls, whether this switch turned it on or not. That is
what we think people expect. Then we recalc light state for each light.
*/
"SwitchOff" -> [
switchName,
StaticData |
logDebug( 'lights', 'Processing switch off (from short press or from UI) for ' + switchName )
val org.eclipse.xtext.xbase.lib.Procedures$Procedure1 RecalcLights = (StaticData as HashMap).get("RecalcLights")
val LightTriggers = (StaticData as HashMap).get("LightTriggers") as HashMap<String, HashMap<String, String>>
val LightTimers = (StaticData as HashMap).get("LightTimers") as HashMap<String, Timer>
val DoublePress = ((StaticData as HashMap).get("DoublePress") as HashMap<String, List<String>>)
DoublePress.get(switchName).forEach[ triggerKey |
LightTriggers.get(triggerKey).clear()
]
if( LightTimers.get(switchName) !== null ){
LightTimers.get(switchName).cancel()
LightTimers.put(switchName, null)
}
RecalcLights.apply( StaticData )
],
/*
Recalc light state for each light (turning it on or off as appropriate based on whether it has any
triggers remaining open)
*/
"RecalcLights" -> [
StaticData |
logDebug( 'lights', 'Recalculating lights' )
val LightTriggers = (StaticData as HashMap).get("LightTriggers") as HashMap<String, HashMap<String, String>>
var String sDebug = ""
LightTriggers.keySet.forEach[ triggerKey |
var SwitchItem swLightOutput = LightOutputs.members.filter( ou | ou.name.startsWith( 'sw' + triggerKey ) ).last as SwitchItem
var SwitchItem swLightInput = LightInputs.members.filter( in | in.name.startsWith( 'sw' + triggerKey ) ).last as SwitchItem
sDebug = sDebug.concat( triggerKey + ":" + LightTriggers.get(triggerKey).size() + " ")
if( LightTriggers.get(triggerKey).size() == 0 ) {
swLightOutput.sendCommand(OFF)
swLightInput.postUpdate(OFF)
} else {
swLightOutput.sendCommand(ON)
}
]
logDebug( 'lights', sDebug )
]
)
/*
Calculate what to do based on a light button press
*/
val ProcessButtonPress = [
switchName,
sEvent,
StaticData |
logDebug( 'lights', 'Processing button press for ' + switchName + ' event was ' + sEvent )
val org.eclipse.xtext.xbase.lib.Procedures$Procedure2 SetLightTimer = (StaticData as HashMap).get("SetLightTimer")
val org.eclipse.xtext.xbase.lib.Procedures$Procedure2 SwitchOff = (StaticData as HashMap).get("SwitchOff")
val org.eclipse.xtext.xbase.lib.Procedures$Procedure1 RecalcLights = (StaticData as HashMap).get("RecalcLights")
val SinglePress = (StaticData as HashMap).get("SinglePress") as HashMap<String, List<String>>
val DoublePress = (StaticData as HashMap).get("DoublePress") as HashMap<String, List<String>>
val LightTriggers = (StaticData as HashMap).get("LightTriggers") as HashMap<String, HashMap<String, String>>
val LightTimers = (StaticData as HashMap).get("LightTimers") as HashMap<String, Timer>
var SwitchItem swLightOutput = LightOutputs.members.filter( ou | ou.name.startsWith( 'sw' + SinglePress.get(switchName).get(0))).last as SwitchItem
// take action depending on what sort of button press we got
switch sEvent {
case 'SHORT_PRESSED': {
// add this button to the triggers for each of the short lights, set a timer for the specified length to turn them off again
logDebug( 'lights', 'We got a short press')
if( swLightOutput.state == OFF){
SinglePress.get(switchName).forEach [ lightOutput |
LightTriggers.get(lightOutput).put(switchName, switchName)
]
RecalcLights.apply( StaticData )
SetLightTimer.apply( switchName, StaticData )
} else { // remove triggers from all lights for this button, cancel timer, then recalc lights
logDebug( 'lights', 'Light currently on, turning off all lights turned on by ' + switchName)
SwitchOff.apply(switchName, StaticData)
}
}
case 'DOUBLE_PRESSED': {
// turn on all the double press lights and set a timer
logDebug( 'lights', 'We got a double press')
DoublePress.get(switchName).forEach[ lightOutput |
LightTriggers.get(lightOutput).put(switchName, switchName)
]
RecalcLights.apply( StaticData )
SetLightTimer.apply( switchName, StaticData )
}
case 'LONG_PRESSED': {
// cancel a timer if there is one, turn on the small set of lights. If the large set of lights are on, they'll stay on
logDebug( 'lights', 'We got a long press')
if( swLightOutput.state == ON){
if( LightTimers.get(switchName) !== null ){
LightTimers.get(switchName).cancel()
LightTimers.put(switchName, null)
} else {
logInfo( 'lights', 'Long press for light thats already on ' + switchName + ' and there was no timer, which could be a problem')
}
}
SinglePress.get(switchName).forEach [ lightOutput |
LightTriggers.get(lightOutput).put(switchName, switchName)
]
RecalcLights.apply( StaticData )
}
case 'TRIPLE_PRESSED': {
// If this light isn't on, turn on all outside lights with a timer. If this light is on, turn off all lights and clear all triggers and all timers
logDebug( 'lights', 'We got a triple press')
if( swLightOutput.state == ON){
LightTimers.keySet.forEach[ timerKey |
if( LightTimers.get(switchName) !== null ){
LightTimers.get(switchName).cancel()
LightTimers.put(switchName, null)
}
]
SinglePress.get("AllOff").forEach[ light |
LightTriggers.get(light).clear()
]
RecalcLights.apply( StaticData )
} else {
SinglePress.get("Triple").forEach[ lightOutput |
LightTriggers.get(lightOutput).put('Triple', switchName)
]
RecalcLights.apply( StaticData )
SetLightTimer.apply( 'Triple', StaticData )
}
}
default: {
logInfo( 'lights', 'Received unhandled event ' + sEvent + ' on item ' + switchName )
}
}
]
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
MOTION SENSORS
*/
// Static Config
/*
Logic flow for motion sensors
Timer already active:
Cancel timer, restart it. Keep the list of lights turned on
Timer not already active:
For each light
Light already on - do nothing
Light not on - turn it on, and remember it was turned on for timer turn off
End of timer:
Turn off all lights that were turned on by this motion sensor
Sensitivity:
Keep track of every new turn on. If it happens more than 3 times in a half hour, turn down sensitivity by 20 points, set
a timer to adjust it back up by 20 points in 4 hours
When adjust back up, if it's not at maxSensitivity, then set another timer to adjust it up another 20 points (or to max sensitivity)
*/
val ProcessMotion = [
motionName,
StaticData |
logDebug( 'motion', 'Processing motion event for ' + motionName )
val org.eclipse.xtext.xbase.lib.Procedures$Procedure1 RecalcLights = (StaticData as HashMap).get("RecalcLights")
val HashMap< String, List<String>> MotionLights = (StaticData as HashMap).get("MotionLights")
val LightTriggers = (StaticData as HashMap).get("LightTriggers") as HashMap<String, HashMap<String, String>>
var StringItem strIllumination = MotionIlluminations.members.filter( il | il.name.startsWith( 'str' + motionName )).last as StringItem
// turn on lights
if(strIllumination.state != "dark") {
logDebug( 'motion', "It's not dark out, it's " + strIllumination.state + " don't turn on lights for " + motionName)
} else {
logDebug( 'motion', "It's dark out, turn on lights")
MotionLights.get(motionName).forEach[ lightOutput |
LightTriggers.get(lightOutput).put(motionName, motionName)
]
RecalcLights.apply( StaticData )
}
// still need to write logic to check the list of trigger events and dial down sensitivity, and set timer to dial sensitivity back up
]
// sensor now inactive, turn off each light that was turned on
val EndMotion = [
String motionName,
StaticData |
logDebug( 'motion', 'Processing end of motion event for ' + motionName )
val org.eclipse.xtext.xbase.lib.Procedures$Procedure2 LightTimerEnd = (StaticData as HashMap).get("LightTimerEnd")
LightTimerEnd.apply( motionName, StaticData )
]
rule "Shelly button flood lounge triggered"
when
Channel "shelly:shellyplus1:a8032aba16f8:relay#button" triggered
then
ProcessButtonPress.apply( "FloodLounge", receivedEvent, StaticData )
end
rule "Shelly button sensor dining triggered"
when
Channel "shelly:shellyplus1:441793aa6bf8:relay#button" triggered
then
ProcessButtonPress.apply( "SensorDining", receivedEvent, StaticData )
end
rule "Shelly button sensor study triggered"
when
Channel "shelly:shellyplus1:a8032aba1704:relay#button" triggered
then
ProcessButtonPress.apply( "SensorStudy", receivedEvent, StaticData )
end
rule "Shelly button sensor laundry triggered"
when
Channel "shelly:shellyplus1:441793a950cc:relay#button" triggered
then
ProcessButtonPress.apply( "SensorLaundry", receivedEvent, StaticData )
end
rule "Shelly button sensor atrium triggered"
when
Channel "shelly:shellyplus1:441793a86d40:relay#button" triggered
then
ProcessButtonPress.apply( "SensorAtrium", receivedEvent, StaticData )
end
rule "Shelly button sensor boot triggered"
when
Channel "shelly:shellyplus1:441793a94fe8:relay#button" triggered
then
ProcessButtonPress.apply( "SensorBoot", receivedEvent, StaticData )
end
rule "Shelly button sensor kitchen triggered"
when
Channel "shelly:shellyplus1:083af2028610:relay#button" triggered
then
ProcessButtonPress.apply( "SensorKitchen", receivedEvent, StaticData )
end
rule "Shelly button flood tractor triggered"
when
Channel "shelly:shellyplus1:441793aa9040:relay#button" triggered
then
ProcessButtonPress.apply( "FloodTractor", receivedEvent, StaticData )
end
rule "Shelly button light boot door triggered"
when
Channel "shelly:shellyplus1:083af202a350:relay#button" triggered
then
ProcessButtonPress.apply( "LightBootDoor", receivedEvent, StaticData )
end
rule "Shelly button light boot hall triggered"
when
Channel "shelly:shellyplus1:083af2029730:relay#button" triggered
then
ProcessButtonPress.apply( "LightBootHall", receivedEvent, StaticData )
end
rule "Shelly button sensor daybed triggered"
when
Channel "shelly:shellyplus2pm-relay:80646fdbe00c:relay1#button" triggered
then
ProcessButtonPress.apply( "SensorDaybed", receivedEvent, StaticData )
end
rule "Shelly button flood master triggered"
when
Channel "shelly:shellyplus2pm-relay:80646fdbe00c:relay2#button" triggered
then
ProcessButtonPress.apply( "FloodMaster", receivedEvent, StaticData )
end
rule "Shelly button sensor garage triggered"
when
Channel "shelly:shellyplus2pm-relay:80646fdb89e0:relay1#button" triggered
then
ProcessButtonPress.apply( "SensorGarage", receivedEvent, StaticData )
end
rule "Shelly button light garage triggered"
when
Channel "shelly:shellyplus2pm-relay:80646fdb89e0:relay2#button" triggered
then
ProcessButtonPress.apply( "LightGarage", receivedEvent, StaticData )
end
rule "Open garage door, turn on garage lights with timer"
when
Item swGarageDoor changed to ON
then
logDebug( 'lights', 'Garage door opened, considering turning on lights, its currently ' + strMotionBootIllumination.state)
if( strMotionBootIllumination.state == 'dark'){
ProcessButtonPress.apply( "LightGarage", "SHORT_PRESSED", StaticData )
}
end
rule "Close garage door, turn off garage lights"
when
Item swGarageDoor changed to OFF
then
logDebug( 'lights', 'Garage door closed, turning off lights' )
val org.eclipse.xtext.xbase.lib.Procedures$Procedure2 LightTimerEnd = StaticData.get("LightTimerEnd")
LightTimerEnd.apply( "LightGarage", StaticData )
end
rule "Manual light turn on from UI"
when
Member of LightInputs received command ON
then
val LightInput = triggeringItem
logDebug( 'lights', 'Light manually turned on for ' + LightInput.name )
val switchName = LightInput.name.substring(2, LightInput.name.length() - 5)
ProcessButtonPress.apply( switchName, "SHORT_PRESSED", StaticData )
end
rule "Manual light turn off from UI"
when
Member of LightInputs received command OFF
then
val LightInput = triggeringItem
logDebug( 'lights', 'Light manually turned off for ' + LightInput.name )
val switchName = LightInput.name.substring(2, LightInput.name.length() - 5)
val org.eclipse.xtext.xbase.lib.Procedures$Procedure2 SwitchOff = StaticData.get("SwitchOff")
SwitchOff.apply( switchName, StaticData )
end
rule "Manual all lights from UI"
// we can triple press on and triple press off, so no need to distinguish on vs off
when
Item swLightTriplePress received command
then
logDebug( 'lights', 'Toggle all outside lights (triple press from UI)' )
ProcessButtonPress.apply( 'FloodMaster', 'TRIPLE_PRESSED', StaticData )
end
rule "Manual flood lights from UI"
// we double press on, but we want to single press off
when
Item swFloodLights received command ON
then
logDebug( 'lights', 'Turn on flood lights from UI' )
ProcessButtonPress.apply( 'FloodLounge', 'DOUBLE_PRESSED', StaticData )
end
rule "Manual flood lights from UI"
// we double press on, but we want to single press off
when
Item swFloodLights received command OFF
then
logDebug( 'lights', 'Turn off flood lights from UI' )
ProcessButtonPress.apply( 'FloodLounge', 'SHORT_PRESSED', StaticData )
end
// Motion rules
rule "Shelly motion boot triggered on"
when
Item swMotionBootActive changed to ON
then
ProcessMotion.apply( "MotionBoot", StaticData )
end
rule "Shelly motion boot triggered off"
when
Item swMotionBootActive changed to OFF
then
EndMotion.apply( "MotionBoot", StaticData )
end
rule "Shelly motion dining triggered on"
when
Item swMotionDiningActive changed to ON
then
ProcessMotion.apply( "MotionDining", StaticData )
end
rule "Shelly motion dining triggered off"
when
Item swMotionDiningActive changed to OFF
then
EndMotion.apply( "MotionDining", StaticData )
end
rule "Shelly motion kitchen triggered on"
when
Item swMotionKitchenActive changed to ON
then
ProcessMotion.apply( "MotionKitchen", StaticData )
end
rule "Shelly motion kitchen triggered off"
when
Item swMotionKitchenActive changed to OFF
then
EndMotion.apply( "MotionKitchen", StaticData )
end
And the items:
Group MotionSensors
Group MotionIlluminations
Group MotionBatteryLevels
Group MotionBatteryLows
Group MotionActives
Group MotionLastMotions
Group MotionDining "Motion Sensor Dining" (MotionSensors)
String strMotionDiningIllumination "Motion Sensor Dining Illumination [%s]" (MotionDining, MotionIlluminations) { channel="shelly:shellymotion:2c1165cb0d29:sensors#illumination" }
Number numMotionDiningBatteryLevel "Motion Sensor Dining Battery Level [%d%%]" (MotionDining, MotionBatteryLevels) { channel="shelly:shellymotion:2c1165cb0d29:battery#batteryLevel" }
Switch swMotionDiningBatteryLow "Motion Sensor Dining Battery Low" (MotionDining, MotionBatteryLows) { channel="shelly:shellymotion:2c1165cb0d29:battery#lowBattery" }
Switch swMotionDiningActive "Motion Sensor Dining Active" (MotionDining, MotionActives) { channel="shelly:shellymotion:2c1165cb0d29:sensors#motion" }
DateTime dtMotionDiningLastMotion "Motion Sensor Dining Last Motion [%1$td %1$tb %1$tr]" <clock> (MotionDining, MotionLastMotions) { channel="shelly:shellymotion:2c1165cb0d29:sensors#motionTimestamp"}
Group MotionKitchen "Motion Sensor Kitchen" (MotionSensors)
String strMotionKitchenIllumination "Motion Sensor Kitchen Illumination [%s]" (MotionKitchen, MotionIlluminations) { channel="shelly:shellymotion:8cf681cd2138:sensors#illumination" }
Number numMotionKitchenBatteryLevel "Motion Sensor Kitchen Battery Level [%d%%]" (MotionKitchen, MotionBatteryLevels) { channel="shelly:shellymotion:8cf681cd2138:battery#batteryLevel" }
Switch swMotionKitchenBatteryLow "Motion Sensor Kitchen Battery Low" (MotionKitchen, MotionBatteryLows) { channel="shelly:shellymotion:8cf681cd2138:battery#lowBattery" }
Switch swMotionKitchenActive "Motion Sensor Kitchen Active" (MotionKitchen, MotionActives) { channel="shelly:shellymotion:8cf681cd2138:sensors#motion" }
DateTime dtMotionKitchenLastMotion "Motion Sensor Kitchen Last Motion [%1$td %1$tb %1$tr]" <clock> (MotionKitchen, MotionLastMotions) { channel="shelly:shellymotion:8cf681cd2138:sensors#motionTimestamp"}
Group MotionBoot "Motion Sensor Boot" (MotionSensors)
String strMotionBootIllumination "Motion Sensor Boot Illumination [%s]" (MotionBoot, MotionIlluminations) { channel="shelly:shellymotion:2c1165cb07b9:sensors#illumination" }
Number numMotionBootBatteryLevel "Motion Sensor Boot Battery Level [%d%%]" (MotionBoot, MotionBatteryLevels) { channel="shelly:shellymotion:2c1165cb07b9:battery#batteryLevel" }
Switch swMotionBootBatteryLow "Motion Sensor Boot Battery Low" (MotionBoot, MotionBatteryLows) { channel="shelly:shellymotion:2c1165cb07b9:battery#lowBattery" }
Switch swMotionBootActive "Motion Sensor Boot Active" (MotionBoot, MotionActives) { channel="shelly:shellymotion:2c1165cb07b9:sensors#motion" }
DateTime dtMotionBootLastMotion "Motion Sensor Boot Last Motion [%1$td %1$tb %1$tr]" <clock> (MotionBoot, MotionLastMotions) { channel="shelly:shellymotion:2c1165cb07b9:sensors#motionTimestamp"}
Group Lights
Group LightInputs
Group LightOutputs
Group LightLastEvents
Switch swLightTriplePress "All outside lights (panic)" (Lights)
Switch swFloodLights "Flood lights" (Lights)
// shelly 2 master - second channel
Group SensorDaybed "Daybed Sensor Light" (Lights)
Switch swSensorDaybedInput "Daybed Sensor Switch" (SensorDaybed, LightInputs)
Switch swSensorDaybedOutput "Daybed Sensor Output" (SensorDaybed, LightOutputs) {channel="shelly:shellyplus2pm-relay:80646fdbe00c:relay1#output"}
String stringSensorDaybedLastEvent "Daybed Sensor Last Event" (SensorDaybed, LightLastEvents) {channel="shelly:shellyplus2pm-relay:80646fdbe00c:relay1#lastEvent"}
// shelly 2 master - first channel
Group FloodMaster "Master Flood Light" (Lights)
Switch swFloodMasterInput "Master Flood Switch" (FloodMaster, LightInputs)
Switch swFloodMasterOutput "Master Flood Output" (FloodMaster, LightOutputs) {channel="shelly:shellyplus2pm-relay:80646fdbe00c:relay2#output"}
String stringFloodMasterLastEvent "Master Flood Last Event" (FloodMaster, LightLastEvents) {channel="shelly:shellyplus2pm-relay:80646fdbe00c:relay2#lastEvent"}
Group SensorStudy "Study Sensor Light" (Lights)
Switch swSensorStudyInput "Study Sensor Switch" (SensorStudy, LightInputs)
Switch swSensorStudyOutput "Study Sensor Output" (SensorStudy, LightOutputs) {channel="shelly:shellyplus1:a8032aba1704:relay#output"}
String stringSensorStudyLastEvent "Study Sensor Last Event" (SensorStudy, LightLastEvents) {channel="shelly:shellyplus1:a8032aba1704:relay#lastEvent"}
Group SensorLaundry "Laundry Sensor Light" (Lights)
Switch swSensorLaundryInput "Laundry Sensor Switch" (SensorLaundry, LightInputs)
Switch swSensorLaundryOutput "Laundry Sensor Output" (SensorLaundry, LightOutputs) {channel="shelly:shellyplus1:441793a950cc:relay#output"}
String stringSensorLaundryLastEvent "Laundry Sensor Last Event" (SensorLaundry, LightLastEvents) {channel="shelly:shellyplus1:441793a950cc:relay#lastEvent"}
Group SensorAtrium "Atrium Sensor Light" (Lights)
Switch swSensorAtriumInput "Atrium Sensor Switch" (SensorAtrium, LightInputs)
Switch swSensorAtriumOutput "Atrium Sensor Output" (SensorAtrium, LightOutputs) {channel="shelly:shellyplus1:441793a86d40:relay#output"}
String stringSensorAtriumLastEvent "Atrium Sensor Last Event" (SensorAtrium, LightLastEvents) {channel="shelly:shellyplus1:441793a86d40:relay#lastEvent"}
Group SensorBoot "Boot Sensor Light" (Lights)
Switch swSensorBootInput "Boot Sensor Switch" (SensorBoot, LightInputs)
Switch swSensorBootOutput "Boot Sensor Output" (SensorBoot, LightOutputs) {channel="shelly:shellyplus1:441793a94fe8:relay#output"}
String stringSensorBootLastEvent "Boot Sensor Last Event" (SensorAtrium, LightLastEvents) {channel="shelly:shellyplus1:441793a94fe8:relay#lastEvent"}
Group SensorKitchen "Kitchen Sensor Light" (Lights)
Switch swSensorKitchenInput "Kitchen Sensor Switch" (SensorKitchen, LightInputs)
Switch swSensorKitchenOutput "Kitchen Sensor Output" (SensorKitchen, LightOutputs) {channel="shelly:shellyplus1:083af2028610:relay#output"}
String stringSensorKitchenLastEvent "Kitchen Sensor Last Event" (SensorKitchen, LightLastEvents) {channel="shelly:shellyplus1:083af2028610:relay#lastEvent"}
Group SensorDining "Dining Sensor Light" (Lights)
Switch swSensorDiningInput "Dining Sensor Switch" (SensorDining, LightInputs)
Switch swSensorDiningOutput "Dining Sensor Output" (SensorDining, LightOutputs) {channel="shelly:shellyplus1:441793aa6bf8:relay#output"}
String stringSensorDiningLastEvent "Dining Sensor Last Event" (SensorDining, LightLastEvents) {channel="shelly:shellyplus1:441793aa6bf8:relay#lastEvent"}
Group FloodLounge "Lounge Flood Light" (Lights)
Switch swFloodLoungeInput "Lounge Flood Switch" (FloodLounge, LightInputs)
Switch swFloodLoungeOutput "Lounge Flood Output" (FloodLounge, LightOutputs) {channel="shelly:shellyplus1:a8032aba16f8:relay#output"}
String stringFloodLoungeLastEvent "Lounge Flood Last Event" (FloodLounge, LightLastEvents) {channel="shelly:shellyplus1:a8032aba16f8:relay#lastEvent"}
Group FloodTractor "Tractor Flood Light" (Lights)
Switch swFloodTractorInput "Tractor Flood Switch" (FloodTractor, LightInputs)
Switch swFloodTractorOutput "Tractor Flood Output" (FloodTractor, LightOutputs) {channel="shelly:shellyplus1:441793aa9040:relay#output"}
String stringFloodTractorLastEvent "Tractor Flood Last Event" (FloodTractor, LightLastEvents) {channel="shelly:shellyplus1:441793aa9040:relay#lastEvent"}
// second channel of shelly 2 in garage
Group SensorGarage "Garage Sensor Light" (Lights)
Switch swSensorGarageInput "Garage Sensor Switch" (SensorGarage, LightInputs)
Switch swSensorGarageOutput "Garage Sensor Output" (SensorGarage, LightOutputs) {channel="shelly:shellyplus2pm-relay:80646fdb89e0:relay2#output"}
String stringSensorGarageLastEvent "Garage Sensor Last Event" (SensorGarage, LightLastEvents) {channel="shelly:shellyplus2pm-relay:80646fdb89e0:relay2#lastEvent"}
// first channel of shelly 2 in garage
Group LightGarage "Garage Internal Light" (Lights)
Switch swLightGarageInput "Garage Light Switch" (LightGarage, LightInputs)
Switch swLightGarageOutput "Garage Light Output" (LightGarage, LightOutputs) {channel="shelly:shellyplus2pm-relay:80646fdb89e0:relay1#output"}
String stringLightGarageLastEvent "Garage Light Last Event" (LightGarage, LightLastEvents) {channel="shelly:shellyplus2pm-relay:80646fdb89e0:relay1#lastEvent"}
Group LightBootDoor "Boot Door Light" (Lights)
Switch swLightBootDoorInput "Boot Door Light Switch" (LightBootDoor, LightInputs)
Switch swLightBootDoorOutput "Boot Door Light Output" (LightBootDoor, LightOutputs) {channel="shelly:shellyplus1:083af202a350:relay#output"}
String stringLightBootDoorLastEvent "Boot Door Light Last Event" (LightBootDoor, LightLastEvents) {channel="shelly:shellyplus1:083af202a350:relay#lastEvent"}
Group LightBootHall "Boot Hall Light" (Lights)
Switch swLightBootHallInput "Boot Hall Light Switch" (LightBootHall, LightInputs)
Switch swLightBootHallOutput "Boot Hall Light Output" (LightBootHall, LightOutputs) {channel="shelly:shellyplus1:083af2029730:relay#output"}
String stringLightBootHallLastEvent "Boot Hall Light Last Event" (LightBootHall, LightLastEvents) {channel="shelly:shellyplus1:083af2029730:relay#lastEvent"}