Want to automatically dim a light when it is turned on

This is my first big step into trying to write a script.

I would like to write a script that will automatically dim a light once it has been turned on. What I am thinking of as a good into into writing something like this is simply:

A)   Is the light on?  If false do nothing.
B)   Light is on, in a loop once per minute reduce the light level by 1.
C)  When light level is 0 quit.

I have looked at this, and a couple issues have come up for me.
#1 How do I make sure only one copy of the script is running?
#2 Do I make a rule/script that is on a timer (say running once a minute) which I can do in a CRON job. Or is it better to have the script executed by an event of an item?
#3 For the life of me I can not figure out how to do something like: “LightLevel = LightLevel -1”

It may be that I want to reduce the light lever by 5 every 5 minutes, so the exact times are not as much of an issue as the how do I go about this.

I have done some work with the rules and time based functions, but nothing in scripts. Have a programming background but not in the likes of Java or PHP, old Fortran / COBOL / BASIC guy. Also know C. Is there a scripting language that would be easier for me to use than another?

Greg

If you use OH3 this pretty much sorts itself out.

But beware - retriggers will queue up, it’s only rule execution that is one-at-a-time. That’s only going to become a problem if you use wait-loops or somesuch, so …

The nature of openHAB is event-driven, so its the natural way to build rules too (though it feels weird if you’ve not programmed this way before)
Repeated rule triggers with cron are best avoided as unnecessary overhead.
Stuff like wait-loops are really to be avoided.

In this case for example, you don’t need a rule to do anything until/unless the Light changes (when it is first turned on)

Break it down a bit
x = existing Light level state
x = x - 1
post x to Light as new command

That offers a clue as to how to do this overall

Trigger from Light level change.
If current level zero, exit
Work out the next target step.
Set up a timer to command that step in N minutes
end of rule.

Because the future command will cause a change, it will afterwards retrigger the rule for the next step.

Hi,

Assuming you use OH3, I cobbled something together in Javascript just to get you started:

//var logger = (logger === undefined) ? Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID) : logger
var ScriptExecution = (ScriptExecution === undefined) ? Java.type("org.openhab.core.model.script.actions.ScriptExecution") : ScriptExecution;
var ZonedDateTime = (ZonedDateTime === undefined) ? Java.type("java.time.ZonedDateTime") : ZonedDateTime;

this.FadeOutTimer = (this.FadeOutTimer === undefined) ? null:this.FadeOutTimer; // initialize var if not exists

//logger.info("Rule running");
//logger.info("Event: " + event);
//logger.info("ItemName: " + event.itemName);
//logger.info("ItemState: " + event.itemState);
//logger.info("ItemCommand: " + event.itemCommand);


if((typeof this.event) != 'undefined' && this.event !== null){  // cron triggers do not have event data
  
  if(this.event.itemState !== undefined){  // itemState for "update" / "Change" triggers
    
    if((this.FadeOutTimer === null || this.FadeOutTimer.hasTerminated()) && this.event.itemState > 0){  // only create timer is there is no (running) timer AND light is on (ignore last update to 0)
      
      this.FadeOutTimer = ScriptExecution.createTimer(ZonedDateTime.now().plusSeconds(2), function(){    // create timer, execute function after 5 minutes
        //logger.info("Rule timer running");
        if(itemRegistry.getItem('Item_Light_Dimmer').getState() > 5){  // test current dimmer value
          this.events.sendCommand("Item_Light_Dimmer",(itemRegistry.getItem('Item_Light_Dimmer').getState() - 5));    // command dimmer to be 5% less than current value
          this.FadeOutTimer.reschedule(ZonedDateTime.now().plusSeconds(2));  // "rerun" after 5 minutes
        }else{
          this.events.sendCommand("Item_Light_Dimmer",0);  // last dim action to make sure it doesn't drop below 0
          this.FadeOutTimer = null;  // stop timer
        }
      });
    }
  }
}

this.event = undefined;  

In this example I used plusSesonds(2) instead of plusMinutes(5) to speed things up a bit.

Rule trigger should be
“Item_Light_Dimmer changed”

As @rossko57 noted, it’s more straightforward to have a simple rule that changes the value and retriggers itself (with an exit condition to prevent an infinite loop).

This approach will also survive an OH restart, as you can also have System started as a trigger to ensure it picks up where it left off (though it’s unlikely to be a concern with this particular scenario). A long-running rule with an internal loop won’t persist.

This is a script that I use to dim the light in the master bedroom. The script runs every five seconds and dims the light seven percent each time it runs. Adjust those parameters to you liking.

//logInfo("LightsOutScript","---------- Started")
var Integer currentBrightness = Integer::parseInt(MasterLight.state.toString)
var Integer newBrightness = currentBrightness - 7
if (newBrightness <= 1) {
   MasterLight.sendCommand(0)
   logInfo("LightsOutScript","LightsOut complete")
   return
}

//logInfo("LightsOutScript","Setting MasterLight to {}", newBrightness.toString)
MasterLight.sendCommand(newBrightness.toString)
createTimer(now.plusSeconds(5)) [ | callScript("LightsOut.script") ]
//logInfo("LightsOutScript","Timer Created, newBrightness is {}", newBrightness.toString)

The following command initiates the script in a rule. The rule sets the light to 90% when the top paddle is double tapped and starts the script to dim the light when the bottom paddle is double tapped.

     //  Double tap of the top paddle sets the light to 90%. 
	 //  Double tap of the bottom paddle starts the dimming.
     switch(MasterScene.state) 	{
       case 1.3 : { sendCommand(MasterLight, 90) }
	   case 2.3 : { 
		    sendCommand(LightsOutInProgress,ON)
			if (MasterLight.state > 70) {
	           sendCommand(MasterLight, 70) 
			}
            createTimer(now.plusSeconds(4)) [ | callScript("LightsOut.script") ]
            logInfo("MasterLightSceneChange","Master scene changed to {}", MasterScene.state.toString) 
			}

OK so I took all of your pieces of advice and looked at running a script off of CRON.

The script runs every minute but does not do too much so delays are not an issue.
Running it off of cron gets a few things fixed right away.
#1 never called twice
#2 reset proof, ie if OH3 reboots itself the script will carry on where it left off
#3 very low overhead. No delay loops or other overhead

For writing the script I used BLOCKY to create it. Who ever made BLOCKY you are a genius. Is is so simple to put together a script, without the need to a lot of syntax knowledge.

Below is the code blocky created for me. Short sweet and to the point. The code drops the brightness level by 5 points every time it is called. When it reaches zero nothing really happens.

var LightLevel;
LightLevel = itemRegistry.getItem('Huecolorlamp_Color').getState();
LightLevel = LightLevel - 5;
if (LightLevel < 0) {
  LightLevel = 0;
}
events.sendCommand('Huecolorlamp_Color', LightLevel);

Greg

I don’t really think cron is the way to go. Avoiding the rule being called twice doesn’t mean much if you’re running it every minute, whereas triggering on state changes (and system start) will only run it when it’s necessary.

Not sure what you are worrying about here.

Can’t dispute that. Does this happen often, is it much of adisaster?

The cron rule runs every minute, needed or not, every hour, every day, for eternity.

Neither do our previous suggestions. “Creating a timer” in openHAB rules means setting up a block for code for future execution, like an alarm clock. Having created it or “set the alarm”, the rule exits.

Having said all that, what works is more important than perfection.

1 Like