I’ve helped a lot of people over the years solve this problem and I can’t find a single one of the threads. So here is how I would do it.
First a note. Sleeps longer than 100 msec is considered a bad idea. Given that your lambda will sit there sleeping for a really long time using Timers is going to be a must.
I’m just typing this in (I don’t have dimmers) but if you test it out and tell me it works I’ll turn it into a design pattern. I’m going to implement this using Expire binding timers.
Hopefully, it is obvious what the new Items I’ve introduced below do. If not please ask. DimmingTimer is configured with expire="2s,command=OFF"
.
rule "Start dimming"
when
Item StartDimming received command
then
DimmingTimer.sendCommand(StartDimming.state) // switching StartDimming to OFF will cancel the dimming
end
rule "Dim next step"
when
Item DimmingTimer received command OFF
then
group.members.filter[light|light instanceof DimmerItem].forEach[light |
val currVal = light.state as Number
val step = DimmingStep.state as Number
val goal = DimmingGoal.state as Number
var nextVal = currVal + step // use a negative step to dim down
if((step > 0 && nextVal > goal) || (step < 0 && nextVal < goal)) nextVal = goal
light.sendCommand(nextVal)
// reschedule the Timer if we haven't reached the goal
if(nextVal != goal && DimmingTimer.state != ON) DimmingTimer.sendCommand(ON)
]
if(DimmingTimer.state != ON) StartDimming.postUpdate(OFF) // reset the start switch to OFF
end
Theory of operation:
You send an ON command to the StartDimming switch to start the dimming behavior. This sets the DimmingTimer (dimming will start in two seconds, if you want it to start right away we will need to move the body of Dim nest step into a lambda. When the Timer goes off we loop through all the Dimmers and increment the Light’s state by the step amount (use a negative step to dim down). If any one of the Dimmers is not at the goal reschedule the Timer.
If you send an OFF command to the StartDiming switch the Timer will be canceled and dimming will stop.
I wanted to make the above as simple as possible. You will likely need to expand upon it to meet your needs.
- Obviously, the goal and step can be stored in a global variable instead of Items or they can be calculated.
- To make this generic for multiple Groups, put the dimming logic into a lambda and call that lambda from a Rule dedicated to that Group
- If you need a variable step size (i.e. the amount of time the timer sleeps) you will need to use a Timer instead of Expire binding
- If you want the dim to occur really fast and/or relatively in synch with all the other lights in the Group you will likely need to implement this in JSR223 instead of Rules DSL (ask @spacemanspiff) for details
- You can use Associated Items DP to store the goal, and step values for a given group which may let you genercise this to support multiple dimming groups.
EDIT: Had a bug in the check for whether we have exceeded the goal which would fail if we are dimming down. It has been corrected above.