Dimmer Rule - How can i use a global dim value (item.state)?

Hi,

i use the following dimmer rule.

If I dim the lamp for example with the Basic UI and then with the wall switch, the lamp jumps in the dim value (visible). Basically the rule works great, except for this one point.

Also longer searches here in the forum and at google brought unfortunately no success, perhaps I used only the wrong terms.

I guess it’s really no big deal. :slight_smile:

I would like to use a global dimming value in my rule, but I am not sure how this works. My idea is simply to use the current dimming value of the item as val/var (item.stat), but unfortunately I have not yet managed to implement this.

I’ve tried various things… for example:

val dimVal = WzDlD.state as Number

But I received the following error

Cannot reference the field 'WzDlD' before it is defined

I have unfortunately never been able to correct this “small” error.
I hope for your creative ideas.

Thanks for your help!

  • Platform information:
    • Hardware: RPI4 4GB
    • OS: latest Raspberry Pi OS 32bit
    • Java Runtime Environment: Zulu Embedded 8.25.0.76-linux-aarch32hf
    • openHAB version: 2.5.6

DIM RULE

val int timeoutMills = 100 
var int dimLevel = 50
var Timer timerWzDlD = null

rule IKEA_Remote_WzDlD

when
Channel "deconz:switch:deconzCore:wohnzimmer-decke:buttonevent" triggered

then

switch(receivedEvent.getEvent())

{
	
case "1002": 		// ON
    {
	WzDlD.sendCommand(50)	
    }


case "2002":		// OFF
    {
	WzDlD.sendCommand(0)	
    }


case "1001": 		// dim brighter
    if (timerWzDlD === null) {
        timerWzDlD = createTimer(now.plusSeconds(0), [ |   
            WzDlD.sendCommand(dimLevel)
            if (dimLevel > 99) dimLevel = 99 
            if (dimLevel == 99) {
                timerWzDlD = null
            } else {
                dimLevel = dimLevel + 1
                timerWzDlD.reschedule(now.plusMillis(timeoutMills)) 
            }
        ])
	}


case "1003": 		// dim stop
    {
	timerWzDlD?.cancel
    timerWzDlD = null
    }


case "2001": 		// dim darker
    if (timerWzDlD === null) {
        timerWzDlD = createTimer(now.plusSeconds(0), [ |   
            WzDlD.sendCommand(dimLevel)
            if (dimLevel < 1) dimLevel = 1
            if (dimLevel == 1) { 
                timerWzDlD = null 
            } else {
                dimLevel = dimLevel - 1
                timerWzDlD.reschedule(now.plusMillis(timeoutMills)) 
            }
        ])
	}


case "2003": 		// dim stop
    {
	timerWzDlD?.cancel
    timerWzDlD = null
    }

}
end

ITEM

Group:Dimmer   WzDlD                              "Wohnzimmer Deckenleuchte [%d]"                                      <light>              (WZ, gLight)

Dimmer         Wohnzimmer_Deckenlampe1            "Wohnzimmer Deckenlampe 1 [%d]"                                      <light>              (WZ, gLight, WzDlD)     {channel="deconz:dimmablelight:deconzCore:wohnzimmer-decke1:brightness"}
Dimmer         Wohnzimmer_Deckenlampe2            "Wohnzimmer Deckenlampe 1 [%d]"                                      <light>              (WZ, gLight, WzDlD)     {channel="deconz:dimmablelight:deconzCore:wohnzimmer-decke2:brightness"} 
Dimmer         Wohnzimmer_Deckenlampe3            "Wohnzimmer Deckenlampe 1 [%d]"                                      <light>              (WZ, gLight, WzDlD)     {channel="deconz:dimmablelight:deconzCore:wohnzimmer-decke3:brightness"}

Maybe you’ve referenced at the wrong place? And another maybe: Perhaps the group won’t hold the state.

Another less duplicate solution (but I did not test the code):

val int timeoutMills = 100 

var int dimLevel = 50
var Timer timerWzDlD = null
var Boolean bOnOff = false

rule IKEA_Remote_WzDlD
when
    Channel "deconz:switch:deconzCore:wohnzimmer-decke:buttonevent" triggered
then
	dimLevel = Wohnzimmer_Deckenlampe1.state as Number // will definitely have a state
    bOnOff = if(receivedEvent.getEvent().substring(0,1)=="1" true else false) // up/down
    switch(receivedEvent.getEvent().substring(3,1)) { // 1,2 or 3
	    case "2": {  		// ON or OFF
			WzDlD.sendCommand(if(bOnOff)50 else 0)	
		}
		case "3": {		// dim stop
			timerWzDlD?.cancel
			timerWzDlD = null
		}
		case "1": {		// dim up or down
			if (timerWzDlD === null)
				timerWzDlD = createTimer(now, [ |   
					WzDlD.sendCommand(dimLevel)
					if(bOnOff) // up
						dimLevel ++ // or dimLevel = dimLevel + 1
					else // down
						dimLevel -- // or dimLevel = dimLevel - 1 
					if (dimLevel > 100 || dimlevel < 0) // reached border
						timerWzDlD = null
					else 
						timerWzDlD.reschedule(now.plusMillis(timeoutMills)) 
				])
		}
	}
end

The code is a bit less straight forward :wink:

1 Like

This group holds only his state of all members have the same value, otherwise it will be UNDEF.
Change it to
Group:Dimmer:AVG WzDlD to get an average value. (You can also have a max/min value)

1 Like

Thank you very much for your suggestions.

I have now managed to integrate the item status into the rule.
But unfortunately it does not work.

Now I use the group with MAX (thanks @ljsquare for the hint) , then I convert the var dimLevelWZDL. The rule works like this.

But unfortunately the value is not updated in the rule. If I change the dim value via the Basci UI and dim again with the wallswitch, the dim value jumps back to the value when I dimmed with the wallswitch. I hope this is understandable.

In MS Code the value is updated just in time (when i use Basc UI or wallswitch), so the rule only uses the dim value that was last set with the wallswitch.

Thank you so much for supporting a beginner.

@Udo_Hartmann: when i have fully understood my code, i will go into the optimization and try out your pro version. :wink:

Group:Dimmer:MAX   WzDlD                              "Wohnzimmer Deckenleuchte [%d]"                                      <light>              (WZ, gLightD)
val int timeoutMills = 100 
var Number dimLevelWZDL = Math.round((WzDlD.state as Number).floatValue/100)*100
var Timer timerWzDlD = null 

rule IKEA_Remote_WzDlD

when
Channel "deconz:switch:deconzCore:wohnzimmer-decke:buttonevent" triggered

then

switch(receivedEvent.getEvent())

{
	
case "1002": 		// ON
    {
	WzDlD.sendCommand(50)	
    }


case "2002":		// OFF
    {
	WzDlD.sendCommand(0)	
    }


case "1001": 		// dim brighter
    if (timerWzDlD === null) {
        timerWzDlD = createTimer(now.plusSeconds(0), [ |   
            WzDlD.sendCommand(dimLevelWZDL)
            if (dimLevelWZDL > 99) dimLevelWZDL = 99 
            if (dimLevelWZDL == 99) {
                timerWzDlD = null
            } else {
                dimLevelWZDL = dimLevelWZDL + 1
                timerWzDlD.reschedule(now.plusMillis(timeoutMills)) 
            }
        ])
	}


case "1003": 		// dim stop
    {
	timerWzDlD?.cancel
    timerWzDlD = null
    }


case "2001": 		// dim darker
    if (timerWzDlD === null) {
        timerWzDlD = createTimer(now.plusSeconds(0), [ |   
            WzDlD.sendCommand(dimLevelWZDL)
            if (dimLevelWZDL < 1) dimLevelWZDL = 1
            if (dimLevelWZDL == 1) { 
                timerWzDlD = null 
            } else {
                dimLevelWZDL = dimLevelWZDL - 1
                timerWzDlD.reschedule(now.plusMillis(timeoutMills)) 
            }
        ])
	}


case "2003": 		// dim stop
    {
	timerWzDlD?.cancel
    timerWzDlD = null
    }

}
end

2020-07-27 12_12_49-wohnzimmer_deckenleuchte.rules - y_ - Visual Studio Code

Global variable declarations run once, evaluating when you load the rules file, then never again. I’m not sure if that was what you expected to happen.

1 Like

@rossko57: Thanks for your reply.

Ok, let me try to explain it another way.

I use the wallswitch to dim at value 30.
Then I go to the Basic UI and dim to value 70.

Now I dim again with the wallswitch and hold the switch to dim down, for example.
Now the rule does not start to dim at the last value (70 - that set with Basic UI) but at 30 (which was last used in the rule).

I just expect that the last set dimming value is always used as a basis value.

Or is that not possible?

Thanks a lot!

Yes, it is possible.

The way it is now, your global variable takes on a value related to your Item’s actual dimmer value only at boot time. End.
You can fiddle with BasicUI or the wall knob as much as you like, it will not change your variable. That was evaluated to some number at file load time. It’s just a number.

What you probably want to do is calculate existing dimmer level when you run the rule.
You can place that into your global variable so that other rules or timer code can also access it later.

1 Like

That sounds great, but please tell me how to do that. I can’t find any code examples that help me, where do i set the gloable variable?

Thanks a lot!

You are already doing it. In your example, timeoutMillis is defined outside of any rule. This makes it a “global”, existing outside of any rule, but accessible to any rule in the same file.

Variables defined inside a rule evaporate when the rule exits.

1 Like

Thanks, but I don’t see how I can make this work. How do i tell the following var to get the current value of the item (WzDlD.state) before the rule starts? because this is missing or doesn’t work that way.

var Number dimLevelWZDL = Math.round((WzDlD.state as Number).floatValue/100)*100

Thanks a lot

You can’t.
Why not tell it to get current value of the Item as the first thing the rule does? That’s when you need the value, isn’t it?

// make a global if you need it that way
var Number dimLevelWZDL = 0
// but who cares what the initial value is

rule IKEA_Remote_WzDlD
when
   Channel "deconz:switch:deconzCore:wohnzimmer-decke:buttonevent" triggered
then
   dimLevelWZDL = Math.round((WzDlD.state as Number).floatValue/100)*100
   // assign an up to date value to your global
...

I’m not sure you need the maths, by the way. Won’t this do?
dimLevelWZDL = WzDlD.state as Number

1 Like

Finally…it works!

Thanks a lot for helping a beginner…great community!

Attached the final rule.

var Number dimLevelWZDL = 2
val int timeoutMills = 100 
var Timer timerWzDlD = null 

rule IKEA_Remote_WzDlD

when

Channel "deconz:switch:deconzCore:wohnzimmer-decke:buttonevent" triggered

then

dimLevelWZDL = WzDlD.state as Number
switch(receivedEvent.getEvent())

{
	
case "1002": 		// ON
    {
	WzDlD.sendCommand(50)	
    }


case "2002":		// OFF
    {
	WzDlD.sendCommand(0)	
    }


case "1001": 		// dim brighter
    if (timerWzDlD === null) 
    {
        timerWzDlD = createTimer(now.plusSeconds(0), [ |   
            WzDlD.sendCommand(dimLevelWZDL)
            if (dimLevelWZDL > 100) dimLevelWZDL = 100 
            if (dimLevelWZDL == 100) {
                timerWzDlD = null
            } else {
                dimLevelWZDL = dimLevelWZDL + 1
                timerWzDlD.reschedule(now.plusMillis(timeoutMills)) 
            }
        ])
	}


case "1003": 		// dim stop
    {
	timerWzDlD?.cancel
    timerWzDlD = null
    }


case "2001": 		// dim darker
    if (timerWzDlD === null) {
        timerWzDlD = createTimer(now.plusSeconds(0), [ |   
            WzDlD.sendCommand(dimLevelWZDL)
            if (dimLevelWZDL < 2) dimLevelWZDL = 2
            if (dimLevelWZDL == 2) { 
                timerWzDlD = null 
            } else {
                dimLevelWZDL = dimLevelWZDL - 1
                timerWzDlD.reschedule(now.plusMillis(timeoutMills)) 
            }
        ])
	}


case "2003": 		// dim stop
    {
	timerWzDlD?.cancel
    timerWzDlD = null
    }

}
end