Increasing light over a given time period

Most other smarthome system has the option to increase the light from one value to another value over a given time, i.e 40% to 100% over 1h. How can I implemnt this as a lambda function in OH?

So I would like to the folowing pseudo code :

dimLight(groupname,optional startvalue = current value), end value, time){
 for each item in group {
         if dimmeablie item{
                  stepsize = (end value -startvalue/time/2)
                  sencdommand(item,currentValue+stepsize)
                  sleep 2s
         }
}
}

I bet someone has done something similiar like this before,

So when my evening rule triggers i would simply call dimLight(Group_LivingRoom,40%,3600s) (use current value as start value)
In morning I would simply call dimLight(Group_MasterBedRoom,100%,600s)

He did something like this with a Xiamoi Button. Maybe this rule can help you

Maybe here is another approach to think about:

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.

2 Likes

So I will give you some more background info

/// ITEMS

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////// Living Room Lights ///////////////////////////////////////////////////////////////////7////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Color LL1 "LL1"(Group_LivingRoom, Group_DMX,Group_DMX_LivingRoom, Group_DMX_Dinner,Group_DMX_Wall,Group_DMX_TV) {dmx="CHANNEL[25,26,27:100000]"}
Switch Light_Kitchen "Kitchen "  (Group_Kitchen,Group_LivingRoom) { gpio="pin:6" } //Relay 4 
 
 and so on....

//Light function items
Switch StartDimming
Switch DimmingTimer

Dimmer Dimmer_LivingRoom "Dimmer [%d %%]" 
Dimmer Dimmer_MasterBedRoom "Dimmer [%d %%]"
Dimmer Dimmer_BathRoom "Dimmer [%d %%]"

My normal light rules, which should be optimized no matter what is here:

and the sitemap is here:

So here is the rule I wrote up to start the dimming:

rule StateRule
when
Item TimeOfDay received command
then
if(TimeOfDay.state.toString == “MORNING”) {
//heatUp
}

else if(TimeOfDay.state.toString == "DAY") {
    //Dim lights for 15 minutes from 0% to 100% 
	dimLights(Group_LivingRoom,100,15*60).apply	
	dimLights(Group_MasterBedRoom,70,15*60).apply
	dimLights(Group_Hallway,85,15*60).apply	

}
else if TimeOfDay.state.toString == "EVENING") {
    //Dim lights for 60 minutes from 100% to 40% 
	dimLights(Group_LivingRoom,40,60*60).apply	
	dimLights(Group_Hallway,40,60*60).apply

end

Then i got a bit lost in your example on how to turn it into a lambda generic design pattern function, do you mind clarifying it?

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////// Dim lights over a given time period to a given value ////////////////7////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 val Functions.Function3 dimLights= [GroupItem dimGroup,number stopValue, number timeToDim |

		StartDimming.sendCommand(ON)
]

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

Please use code fences. It is really hard to read the code.

```
code goes here
```

Converting it to a lambda is simple. Like I said above, only the dimming logic goes into the lambda. That means only the stuff inside the “Dim nest step” goes into the lambda.

import org.eclipse.xtext.xbase.lib.Functions

val Functions$Function5<GroupItem, Number, Number, SwitchItem, SwitchItem, Boolean> dimLights = [group, step, goal, timer, dimming |
    group.members.filter[light|light instanceof DimmerItem].forEach[light |
        val currVal = light.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 && timer.state != ON) timer.sendCommand(ON) 
    ]
    if(timer.state != ON) dimming.postUpdate(OFF) // reset the start switch to OFF
    true
]

rule "Dimming rule"
when
    Item StartDimmingLights1 received command
then
    DimmingLights1Timer.sendCommand(StartDimmingLights1.state)
end

rule "Lights 1 Dim next step"
when
    Item DimmingLights1Timer received command OFF
then
    dimLights.apply(DimmingLights1, DimmingLights1Step.state as Number, DimmingLights1Goal.state as Number, DimmingLights1Timer, StartDimmingLights1)
end

// repeat the above rules for your other groups of lights
// Make sure to set the step and goal before sending ON to the Start Items.

I used the openhab rule fence code, which obvisoly does not work well, went back and fixed it all…