Automatic light dimming

Hello all,
I have the following setup:

  • Openhab running on RPi3 with AEON Z-Wave Stick gen5
  • Z-Wave Fibaro Dimmers connected to ceiliing lights
  • Fibaro multi sensor (motion, light, temperature)

The dimmers work accordingly (e.g. dimming to various % and ON/OFF), however I have tried to implement an automatic light dimming system and the behaviour of the lights/dimmers is not as intended. The main problems:

  1. The lights are set to increase/decrease in steps of 3%: this does not always happen; at times the lights clearly increase/decrease by large steps (e.g from 10% to 50%)

  2. The “automatic light dimming” should start when: the LUX in the room is a certain value, a virtual switch is ON (standard_ALD) and some WiFi presence

However, the lights keep on dimming automatically even when the virtual switch is changed to OFF.

Rule:

/* Automatic Light Dimming */

var Number Dimmer_1_Value = 0
var Number Dimmer_2_Value = 0
var Number Dimmer_3_Value = 0
var Number Dimmer_4_Value = 0

rule "Dimming Up: Entrance"
when
        	Item ZwaveEye1Lux changed	
then
        if (((ZwaveEye1Lux.state as DecimalType) <= ( LuxSetpoint.state as DecimalType - 30)) && (Standard_ALD.state == ON) && (Presence_Mobile_Mike.state == ON || Presence_Iris_Saxion.state == ON || Presence_Pepijn_Saxion.state == ON || Presence_Diamant_Saxion.state == ON || Presence_Euan_Saxion.state == ON ))
        {
                
		
		Dimmer_1_Value = Dimmer_1.state as PercentType
		Dimmer_1_Value = Dimmer_1_Value + 3
		Dimmer_2_Value = Dimmer_2.state as PercentType
		Dimmer_2_Value = Dimmer_2_Value + 3
		Thread::sleep(5000)
		sendCommand(Dimmer_1,Dimmer_1_Value)
                sendCommand(Dimmer_2, Dimmer_2_Value)
		
        }

end

rule "Dimming Down: Entrance"
when
        	Item ZwaveEye1Lux changed	
then
        if (((ZwaveEye1Lux.state as DecimalType) >= ( LuxSetpoint.state as DecimalType + 30)) && (Standard_ALD.state == ON) && (Presence_Mobile_Mike.state == ON || Presence_Iris_Saxion.state == ON || Presence_Pepijn_Saxion.state == ON || Presence_Diamant_Saxion.state == ON || Presence_Euan_Saxion.state == ON ))
        {
		
                Dimmer_1_Value = Dimmer_1.state as PercentType
		Dimmer_1_Value = Dimmer_1_Value - 3
		Dimmer_2_Value = Dimmer_2.state as PercentType
		Dimmer_2_Value = Dimmer_2_Value - 3
		Thread::sleep(5000)
		sendCommand(Dimmer_1,Dimmer_1_Value)
                sendCommand(Dimmer_2, Dimmer_2_Value)
		
		
        }
	
        
end

Items:

Number ZwaveEye1Lux                 "Motion Sensor [%.2f Lux]"    <sun>                     { channel="zwave:device:710617ff:node10:sensor_luminance" }
Number ZwaveEye1Battery             "Motion Sensor [%d %%]"      <battery>          { channel="zwave:device:710617ff:node10:battery-level" }
Number ZwaveEye1TempProxy           "Motion Sensor [%.1f °C]"     <temperature>        { channel="zwave:device:710617ff:node10:sensor_temperature" }
Number ZwaveEye1Temp                "Motion Sensor [%.1f °C]"     <temperature>    (Temperature)
Contact ZwaveEye1Motion              "Motion Sensor [%s]"          <present>     { channel ="zwave:device:710617ff:node10:sensor_binary" }
Switch ZwaveEye1Alarm               "Motion Sensor Alarm [%s]"             <fire>         { channel="zwave:device:710617ff:node10:alarm_general" }
Switch ZwaveEye1AlarmBurglar        "Motion Sensor Alarm b [%s]"             <fire>         { channel="zwave:device:710617ff:node10:alarm_burglar" }
Number ZwaveEye1Seismic             "Motion Sensor Seismic [%f]"             <present>         { channel="zwave:device:710617ff:node10:sensor_seismicintensity" }

Number ZwaveEye2Lux                 "Motion Sensor 2 [%.2f Lux]"    <sun>                     { channel="zwave:device:710617ff:node11:sensor_luminance" }
Contact ZwaveEye2Motion              "Motion Sensor [%s]"          <present>     { channel ="zwave:device:710617ff:node11:sensor_binary" }


/* Virtual */

Number LuxSetpoint	"Setpoint at [%.1f Lux]" <sun>
Switch Energy_Saving	"Setpoint at [30 %%]" <sun>
Switch Entrance_Only	"Setpoint at [60 %%]" <sun>
Switch Living_Room_Only	"Setpoint at [60 %%]" <sun>
Switch Standard_ALD	"Standard Mode" <sun>
Switch test	"test switch" <sun>


/* Dimmers */

Dimmer Dimmer_1      "Dimming Level [%.1f %%]" <slider>  { channel="zwave:device:710617ff:node6:switch_dimmer"}
Number Dimmer_1_Power	"Power Dimmer 1 [%.1f W]" <poweroutlet>  { channel="zwave:device:710617ff:node6:sensor_power"}

Dimmer Dimmer_2      "Dimming Level [%.1f %%]" <slider>  { channel="zwave:device:710617ff:node7:switch_dimmer"}
Number Dimmer_2_Power	"Power Dimmer 2 [%.1f W]" <poweroutlet>  { channel="zwave:device:710617ff:node7:sensor_power"}

Any help would be greatly appreciated!

Regards

Mike

I would logInfo() so that you can see when each rule has been triggered.
Might as well use those to log the values of the compared lux Items too.

Bear in mind there is no lock on your rules - if lux changes again while a rule is waiting in a sleep, another copy of the rule is fired up and actioned. You may have a whole bunch running in parallel.

1 Like

I agree with @rossko57, I bet you have multiple instances of the rule running at the same time.

I’d rewrite your rule to be something more like:

Items
Put all your Presence Items into the same group. See the following for an example. I’ll call it gPresent.

import java.util.concurrent.locks.ReentrantLock

var ReentrantLock entranceLock = new ReentrantLock()

rule "Dimming UP: Entrance"
when
    Item ZwaveEye1Lux changed
then
    if(!entranceLock.isLocked) {
        try {
            if(ZwaveEye1Lux.state < LuxSetpoint.state &&
               Standard_ALD.state == ON &&
               gPresent.state == ON ){
                Thread::sleep(5000)
                Dimmer_1.sendCommand((Dimmer_1.state as Number) + 3)
                Dimmer_2.sendCommand((Dimmer_2.state as Number) + 3)
            }
        } 
        catch(Throwable t) { entranceLock.unlock }
        finally { entranceLock.unlock }
    }
end

The above will ignore any change to ZwaveEye1Lux while there is an instance of the rule running.

2 Likes

Thanks alot for the help! funnily enough I was just reading your thread regarding locks on the back of @rossko57 advice! I will try your suggested code on monday and provide feedback in case anyone else is interested.

Something looks a bit off with this code snippet…

  1. You check if the lock is locked, but never actually lock it yourself
  2. You try to unlock in both the catch clause and the finally clause
  3. You are catching Throwable

To solve the second point, remove entranceLock.unlock from the catch. The finally clause will always run, even after a catch block. The catch of Throwable becomes useless as there’s nothing to do. It’s bad practice to catch Throwable anyway, because that includes things like OutOfMemoryError which you are not handling and therefore should be propagated up the call stack rather than being masked.

To solve the first point, you have a few choices, depending on your intention. If you want to always run the rule body, and if necessary wait for the lock to be released by whoever currently has it, then do something like this:

entranceLock.lock
try {
    // ... whatever
// if required:
// } catch (WhateverException ex) {
//     Do something...
} finally {
    entranceLock.unlock
}

An alternative rule, if you don’t want to do anything if another thread has the lock, is to do something like this:

if (entranceLock.tryLock) {
    try {
        // ... whatever
    // if required:
    // } catch (WhateverException ex) {
    //     Do something...
    } finally {
        entranceLock.unlock
    }
}

NB I’m away from my computer so this is just off the top of my head, typed on my iPad. Hopefully there are no errors!

See the javadocs for ReentrantLock for more info. It might be more appropriate to use lockInterruptibly() rather than lock() but I’ve not tried that.

1 Like
  1. Is indeed a mistake. I remember writing the lock but must have weed it out when I copied and pasted. One of the problems of wiring coffee on a phone.

  2. Based on experience, at least from 1.8 I’ve seen inconsistent behavior with finally NOT being executed when an exception actually occurs. This may be fixed now but I got in the habit of unlocking in both because of things experience. That is how finally is supposed to work but that isn’t how it worked in the Rules DSL at some point. I spent about a man week figuring this out the hard way.

  3. Indeed in normal Java One shouldn’t catch Error, but I do this so that I will at least get a chance to see what rule caused the Error. Otherwise there is nothing in the log to let you correlate the Error with the rule. This is all theoretical of course as I’ve never seen an Error in OH. I will agree that if a Error is caught it should be rethrown. However that again brings 2 to the for as, at in my experience, the finally will not execute as it should.

1 Like

a.) Regarding the “Generic Presence Detection”:

Rule (identical to your example tutorial apart from && replacing “and” in the else if statement):

rule "System started"
when
    System started
then
    Present.sendCommand(OFF) // assume no one is home on startup
end

rule "gPresent updated, at least one sensor changed state"
when
    Item gPresent received update
then
    // someone came home
    if(gPresent.state == ON && Present.state != ON) {
        Present_Timer.postUpdate(OFF) // cancel the timer if necessary
        Present.sendCommand(ON)
    }

    // no one is home
    else if(gPresent.state == OFF && Present.state != OFF){
        Present_Timer.sendCommand(ON) // start the timer
    }
end

rule "Present_Timer expired"
when
    Item Present_Timer received command OFF
then
    Present.sendCommand(OFF)
end

Items:

Switch Present "Someone is Present" <present> // master presence switch
Group:Switch:OR(OFF,ON) gPresent <present> // all presence sensors belong to this group
Switch Present_Timer { expire="1m,command=OFF" }


Switch Presence_Mobile_Mike "Mikes Mobile" <network> (gPresent) { channel="network:device:192_168_2_163:online" }

Switch Presence_Iris_Saxion "Iris Mobile" <network> (gPresent) { channel="network:device:192_168_2_191:online" }

Switch Presence_Diamant_Saxion "Diamant Mobile"  <network> (gPresent) { channel="network:device:192_168_2_146:online" }

Switch Presence_Pepijn_Saxion "Pip Mobile" <network> (gPresent) { channel="network:device:192_168_2_150:online" }

Switch Presence_Euan_Saxion "Euan Mobile" <network> (gPresent) { channel="network:device:192_168_2_117:online" }

My understanding is that the switch “Present” should be ON if anyone of the WiFi connections in the group (gPresent) are connected right? At the moment, there are two main issues;

  1. The switch “Present” is only ON when all the WiFi connections in the gPresent group are connected, thus it is acting as an AND switch rather than an OR switch

  2. The switch “Present” does not switch OFF when all WiFi connections are off, in fact it only switches OFF on restart (as per the rule)

However, while checking the log (events.log) the group gPresent receives the ON/OFF commands correctly (e.g. `gPresent changed from ON to OFF through Presence_Mobile_Mike``) and the Timer is also sent the ON command correctly, but it does not change the state of the switch after the 1 minute.

Any ideas?

Update: Infact according to the log it appears that the the item “Present_Timer” receives the ON command multiple times once all WiFi connections are off:

2017-06-28 12:31:12.427 [ItemCommandEvent          ] - Item 'Present_Timer' received command ON
2017-06-28 12:31:16.409 [ItemStateChangedEvent     ] - Presence_Mobile_Mike changed from ON to OFF
2017-06-28 12:31:16.423 [ItemStateChangedEvent     ] - network_device_192_168_2_163_online changed from ON to OFF
2017-06-28 12:31:16.429 [ItemStateChangedEvent     ] - network_device_192_168_2_163_time changed from 304.38349599999997963095665909349918365478515625 to -1
2017-06-28 12:31:16.437 [ItemCommandEvent          ] - Item 'Present_Timer' received command ON
2017-06-28 12:32:06.401 [ItemStateChangedEvent     ] - network_device_192_168_2_164_time changed from 1.0822860000000000813002998256706632673740386962890625 to 0.4622370000000000089812601800076663494110107421875
2017-06-28 12:32:12.430 [ItemCommandEvent          ] - Item 'Present_Timer' received command ON
2017-06-28 12:32:12.442 [ItemCommandEvent          ] - Item 'Present_Timer' received command ON

However the timer never receives the OFF command and thus Presence does not switch OFF either. I am thinking the error is related to the expire binding at this point? Is there anything that has to be configured for this binding?

The Group gPresent should be either OR(ON,OFF) or AND(OFF,ON). OR(OFF,ON) means if any one Switch is OFF, gPresent is OFF.

Do you have the Expire binding installed?

Thanks for the clarification of the OR.

Indeed the expire binding is installed (via paper UI)

Hi,

First thing I would do (not being an authority of any sort) is to analyze the setup. Z-wave networks can sometimes create a lot of delay in terms of actual response (depending on the routing nodes).
Second thing I would think about is as @rlkoshak and @rossko57 stated, triggered rule execution.
Third thing I am thinking about is that the way you are trying to achieve a constant lighting setup. The way you have written the rule will for sure expose your measured light level values to a lot of unsatisfying values! The control method is purely wrong (at least in my opinion). If it works for you, then keep it as it is, if not, find a control method that can fulfill the need, like PID control!

Best regards,
George

I’m not sure what could be wrong. There is no additional setup required for the Expire binding.

Thanks for the feedback. In regards to the control system; indeed I agree it is rather rudimentary (simple negative feedback loop) - however implementing a control system such as PID would involve finding the transfer function and then tuning the various parameters with unit step response etc. which to be done efficiently requires software (such as 20sim) and/or matlab and at the moment I simply do not have the time for it - furthermore, how would the PID actions be implemented in OpenHAB rules? is there an in built function in the rules or something similar? If you have a link to some good content regarding PID and OpenHab I would be very interested in reading further upon it. Cheers!

Hi @miccio,

A few months ago I pull requested a PID controller binding. Take a look here.
I have tested it with constant lighting, ambient temperature control, and other relatively fast processes (loop time less than 200ms) - for my setups it worked like a charm!.
I am glad I find someone to be interested in PID control, I can provide the jar when requested (momentarily I am trying to keep the tests of the binding applied to my existing running projects, that is why I am not publicly promoting this).

Best regards,

George

3 Likes

Hi there,

I was wondering if you have any ideas why the following code is not working as intended. From what I can tell the, entranceLock never unclocks ; what happens is that the dimmers increase once, then nothing happens anymore.

import java.util.concurrent.locks.ReentrantLock

var java.util.concurrent.locks.ReentrantLock entranceLock  = new java.util.concurrent.locks.ReentrantLock()
var Number Dimmer_1_Value = 0
var Number Dimmer_2_Value = 0
var Number Dimmer_3_Value = 0
var Number Dimmer_4_Value = 0


rule "Dimming UP: Entrance"
when
    Item ZwaveEye1Lux changed
then
    if(!entranceLock.isLocked) {
	entranceLock.lock()
        try {
            if(ZwaveEye1Lux.state as DecimalType <= LuxSetpoint.state as DecimalType &&
               Standard_ALD.state == ON &&
               Present.state == ON){
                Dimmer_1_Value = Dimmer_1.state as PercentType
		Dimmer_1_Value = Dimmer_1_Value + 3
		Dimmer_2_Value = Dimmer_2.state as PercentType
		Dimmer_2_Value = Dimmer_2_Value + 3
		Thread::sleep(1000)
		sendCommand(Dimmer_1,Dimmer_1_Value)
                sendCommand(Dimmer_2, Dimmer_2_Value)
		
            }
        } 
        
	catch(Throwable t) { entranceLock.unlock() }
        finally { entranceLock.unlock() }
    }
end

Any insight would be greatly appreciated!

What is in your logs? Add some logInfos to trace progress and expected values. Errors in try blocks don’t always seem to end up at the catch or finally blocks in rules.

Absolutely add some logging to the main try, the catch, and the finally. In the catch, log out t.toString so we can see what is being thrown.

Try loading it in Eclispe Smarthome Designer to see if there is some syntax problem that is not apparent.

Finally, this isn’t the cause of an error, but when you import java.util.concurrent.locks.ReentrantLock, you only need to refer to it as ReentrantLock in your code. That is the main purpose of imports.

Hello, I will add the logs as soon as I can. Yes I tried various options to make the code work including the import declaration you mention (I saw it in some example code), I did think it didn’t make sense but I still tried it! Anyway, regarding this line of code:

   if(ZwaveEye1Lux.state as DecimalType <= LuxSetpoint.state as DecimalType &&
               Standard_ALD.state == ON &&
               Present.state == ON){
                Dimmer_1_Value = Dimmer_1.state as PercentType
		Dimmer_1_Value = Dimmer_1_Value + 3
		Dimmer_2_Value = Dimmer_2.state as PercentType
		Dimmer_2_Value = Dimmer_2_Value + 3
		Thread::sleep(1000)
		sendCommand(Dimmer_1,Dimmer_1_Value)
                sendCommand(Dimmer_2, Dimmer_2_Value)
		
            }
        } 

If the virtual switch (Standard_ALD ) is switched OFF while the rule is running, then technically the rule should not execute the next time the if statement is cheked, as the dimmers keep on receiving commands even if the switch is turned OFF. The rule initially responds to the switch, as the dimmers only start receiving commands once the switch is turned ON. Any ideas? I wouldn’t think this should happen regardless of the lock/unlock issue?

Like has been suggested, you need to add log statements all over the place in this rule to see exactly where it is getting to, the states of your Items, at each part of the rule.

Dealing with concurrency is really hard and doing it blind is all but impossible.

Hi @miccio,

Do not lose hope, take things to be done step by step. As Rich is always stating, posting logs is most of the times the only way to go!

Best regards,

George

Fair enough, it makes sense! I will post an update once I can. I just thought a switch being OFF (thus the IF condition not being met) would cause the rule to stop running! Anyway thanks for the support everyone, this community is really great!