How I have automated my lights

@boconnor did you ever have time to update your code? I’m working on motion control for lighting in a house I just finished building and would love to see where you ended up with yours. It seems like what you’re doing is exactly what I’m looking to accomplish. I’d love to see the updated product! :slight_smile:

Here is a similar but more modern and robust version…

It is with great shame that I admit that I have not. I just seem to be in a stage of life where tinkering on things like this is a luxury I just don’t have enough time for. I have a z-wave network that doesn’t quite work reliably and when I have had time to work on openHAB it has been spent trying to get my stupid z-wave system actually working reliably. Hopefully over the holidays I will solve that and get back to my lighting code, but don’t hold your breath.

Sorry.

I would love to see the final product. I’m also tinkering arround with the aeotec sensor and a light bulb in my bedroom.
Which is also very special situation because the light isn’t allowed to switch on when I’m asleep but due to my movements (while im sleeping) it tends to switch on.
My GF is getting frustrated from this :stuck_out_tongue: I’m playing arround with time schedules and the sensitivity but so far it’s a nightmare.

@boconnor NO shame there! This is a hobby after all, right? :slight_smile: I actually got it working, with a few exceptions, based on @rlkoshak’s Groups example from post#6 above. I’ve still got to figure out how to work around the different behavior of the Aeotec Multisensor, as I’m using the same sensor you are. I’ve also got to take a few minutes and work out a bug with my TimeOfDay rule. I set it up on day 2 of my journey with OH and clearly didn’t understand it at that point. I’ll have the TOD thing worked out this morning, as for the Multisensor, we’ll see… :slight_smile:

@5iver Thanks Scott. I’d come across this a few times but haven’t really looked at it yet. I definitely will check it out too. I plan to finish working on the current exercise if for no other reason than to learn from it, and then at some point in the future give your method a shot. :slight_smile:

1 Like

@robbie_demaegdt I’ll post my version with items/rules/sensor settings when it get it debugged. I plan to eventually add a “Goodnight” switch, or something like that, to be sure this doesn’t occur. My wife would be quite angry if I let that happen, lol.

If you’re using the example code from post#6, then it “should” be as simple as setting your NIGHT levels to 0 for that particular light. Haven’t quite got there yet to test at that level of detail though. Stay tuned…

@boconnor and @robbie_demaegdt as promised here’s my finished product.

Sensor: Aeotec Multisensor 6
Parameter 3: Motion Sensor Reset Timeout - 10 seconds

Items

Dimmer MFMBRCloset_Dimmer          "Master Closet Light Dimmer [%d %%]"  <slider>   (MF_MstrBed, GSW_Lights, GSW_IndoorLights, InsideMotionLights, mapdbPersist, InfluxDB)      {channel="zwave:device:15d73a75:node17:switch_dimmer"}
Dimmer MFMBRCloset_Motion_Light    "MBR Closet Motion Light"                        (InsideMotionLights)                                                                        {channel="zwave:device:15d73a75:node17:switch_dimmer"}
Switch MFMBRCloset_Motion_Override "MBR Closet Motion Control Override"             (LightMotionControls, mapdbPersist, InfluxDB)
Switch MFMBRCloset_Motion_Pause    "MBR Closet Motion Control Pause"                (LightMotionControls, mapdbPersist, InfluxDB)
Switch MFMBRCloset_Motion_Sensor   "Master Bath Motion"                  <motion>   (LightMotionSensors, mapdbPersist, InfluxDB)                                                {channel="zwave:device:15d73a75:node20:alarm_motion"}
Number MFMBRCloset_DAY_Level       "Daytime lighting level [%s]"         <light>    (LightMotionLevels, mapdbPersist)  
Number MFMBRCloset_EVENING_Level   "Evening lighting level [%s]"         <light>    (LightMotionLevels, mapdbPersist)  
Number MFMBRCloset_NIGHT_Level     "Night lighting level [%s]"           <light>    (LightMotionLevels, mapdbPersist)  
Number MFMBRCloset_DAY_Timeout     "Daytime lighting timeout [%s]"       <light>    (LightMotionTimeouts, mapdbPersist)  
Number MFMBRCloset_EVENING_Timeout "Evening lighting timeout [%s]"       <light>    (LightMotionTimeouts, mapdbPersist)  
Number MFMBRCloset_NIGHT_Timeout   "Night lighting timeout [%s]"         <light>    (LightMotionTimeouts, mapdbPersist)
Dimmer MFMBRCloset_Light_Virtual   "Virtual light switch"                           (MotionLightsVirtual, mapdbPersist)
Number MFMBRCloset_Dim             "dim level for motion lighting [%s]"             (LightMotionDims, mapdbPersist)

Rules

import java.util.Map


val Map<String, Timer> timers = newHashMap // because you will want one timer per room
val Map<String, Timer> ctrlTimers = newHashMap

rule "Motion detected"
when
    Member of LightMotionSensors changed to ON // members are named Room_Motion_Sensor
then
    logInfo("Rule Info", "Step 1 of motion detected")
    if(triggeringItem.state == null) return; // we don't care about NULL

    val room = triggeringItem.name.split("_").get(0)
    val pause = LightMotionControls.members.findFirst[ p | p.name == room + "_Motion_Pause" ] //Items named room_Motion_Pause go in the LightMotionControls Group
    val override = LightMotionControls.members.findFirst[ ov | ov.name == room + "_Motion_Override"] //Items named room_Motion_Overide go in the LightMotionControls Group
    logInfo("Rule Info", "Step 2 of motion detected"+" room: "+ room +" control: "+ pause)
    if(pause.state == ON || override.state == ON) return; // ignore the update if pause or override is ON

    // Get the values and associated Items
    val timer = timers.get(triggeringItem.name)
    val level = LightMotionLevels.members.findFirst[ ll | ll.name == room + "_" + TimeOfDay.state + "_Level" ].state as Number
    val timeoutMins = (LightMotionTimeouts.members.findFirst[ tv | tv.name == room + "_" + TimeOfDay.state + "_Timeout" ].state as Number).intValue
    val virtual = MotionLightsVirtual.members.findFirst[ v | v.name == room + "_Light_Virtual" ]
    val dimLevel = LightMotionDims.members.findFirst[ dim | dim.name == room + "_Dim" ].state as Number
    logInfo("Rule Info", "Step 3 of motion detected" +" timer: "+  timer +" level: "+ level +" timeoutMins: "+ timeoutMins +" virtual: "+ virtual +" dimLevel: "+ dimLevel)
    // Timer is already running
    if(timer !== null && triggeringItem.state == ON) {
        logInfo("Rule Info", "Step 4 of motion detected rule.  Send "+ level +" to "+ virtual +" and reschedule the timer")
        virtual.sendCommand(level) // undo the dim if necessary
        timer.reschedule(now.plusMinutes(timeoutMins)) // goes off timeoutMins minutes after the last time the motion sensor detects motion
    }
    // No Timer was running
    else {
        logInfo("Rule Info", "Step 5 of motion detected rule.  No light timer running for this room.  Turn on " + room + " lights")
        virtual.sendCommand(level) // turn on the light

        // Create a timer to dim then turn off the light
        timers.put(triggeringItem.name, createTimer(now.plusMinutes(timeoutMins), [ |  

            // if already dimmed then turn off
            if(virtual.state == dimLevel) {
                logInfo("Rule Info", "Step 6 of motion detected rule.  Turn off light after dim-before-off sequence. " + triggeringItem.name + " = " + triggeringItem.state)
                logInfo("Rule Info", "Step 6 of motion detected rule: virtual: "+ virtual.state +" dimLevel: "+ dimLevel + " triggeringItem.state: "+ triggeringItem.state )
                virtual.sendCommand(OFF)
                timers.get(triggeringItem.name).cancel // cancel the timer
                //timers.put(triggeringItem.name, null)  // clear the timer
                
            }

            // dim the light and reset the timer for another minute
            else {
                logInfo("Rule Info", "Step 7 of motion detected rule.  Begin dim-before-off sequence.")
                logInfo("Rule Info", "Step 7 of motion detected rule: virtual: "+ virtual.state +" dimLevel: "+ dimLevel +" timer: "+ timers.get(triggeringItem.name) +" now: "+ now)
                virtual.sendCommand(dimLevel)
                timers.get(triggeringItem.name).reschedule(now.plusMinutes(1))
                }
            ])
        )}
end

rule "Pause motion control if light changed by switch"
when
    Member of InsideMotionLights changed  
then
    val room = triggeringItem.name.split("_").get(0)
    val virtual = MotionLightsVirtual.members.findFirst[ v | v.name == room + "_Light_Virtual" ] //Items named room_Light_Virtual go in the MotionLightsVirtual Group
    val pause = LightMotionControls.members.findFirst[ p | p.name == room + "_Motion_Pause" ]  //Items named room_Motion_Pause go in the LightMotionControls Group
    logInfo("Rule Info", "Step 1 of pause motion control" +" room: "+ room +" virtual: "+ virtual +" control: "+ pause)
    if(triggeringItem.state != virtual.state) {
        logInfo("Rule Info", "Step 2 of pause motion control rule.  Set ROOM_Motion_Control to Pause")
        virtual.postUpdate(triggeringItem.state) // we don't want this to be treated as a new command
        pause.sendCommand(ON) // temporarily disable the motion sensor control of the light

        val int disabledTime = if(triggeringItem.state == 0) 10 else 60 //If light is OFF, set to 10 minutes.  If ON, set to 60 minutes.
        val timer = ctrlTimers.get(triggeringItem.name)
        if(timer !== null){
            logInfo("Rule Info", "Step 3 of pause motion control rule.  Add "+ disabledTime +" time to Motion_Control pause for "+ room)
            timer.reschedule(now.plusMinutes(disabledTime))
        } 
        else {
            ctrlTimers.put(triggeringItem.name, createTimer(now.plusMinutes(disabledTime), [ |
                logInfo("Rule Info", "Step 4 of pause motion control rule.  Pause Motion Control for "+ room)
                pause.sendCommand(OFF)
                ctrlTimers.put(triggeringItem.name, null)
            ])
            )}
    }
end

rule "Motion control resume"
when
    Member of LightMotionControls received command OFF
then
    val room = triggeringItem.name.split("_").get(0)
    val motion = LightMotionSensors.members.findFirst[ m | m.name == room + "_Motion_Sensor" ]
    val light = InsideMotionLights.members.findFirst[ l | l.name == room + "_Motion_Light" ]
    val dimLevel = LightMotionDims.members.findFirst[ dim | dim.name == room + "_Dim" ].state as Number
    val virtual = MotionLightsVirtual.members.findFirst[ v | v.name == room + "_Light_Virtual" ]
    logInfo("Rule Info", "Step 1 of motion control resume" +" room: "+ room +" motion: "+ motion +" light: "+ light +" dimLevel: "+ dimLevel +" virtual: "+ virtual)
    if(timers.get(motion.name) !== null || light.state == 0) return; // a timer is already set to handle the light or the light is already off
    logInfo("Rule Info", "Step 2 of motion control resume")
    if(light.state == dimLevel) return; // Light is already at the dim level
    logInfo("Rule Info", "Step 3 of motion control resume")
    timers.put(motion.name, createTimer(now.plusMinutes(1), [ |
        if(triggeringItem.state == ON) virtual.sendCommand(OFF)
        timers.put(motion.name, null)
    ]) 
    )
end

rule "Update real from virtual"
when
    Member of MotionLightsVirtual changed  
then
    val room = triggeringItem.name.split("_").get(0)
    val virtual = MotionLightsVirtual.members.findFirst[ v | v.name == room + "_Light_Virtual" ]
    val light = InsideMotionLights.members.findFirst[ l | l.name == room + "_Motion_Light" ]
    logInfo("Rule Info", "Step 1 update real from virtual rule.  "+" room: "+ room +" virtual: "+ virtual +" control: "+ light)
    light.sendCommand(virtual.state.toString)
end

This works pretty much the same as the OP with a couple of minor tweaks. I ended up removing a couple of the “HouseMode” items and reducing my TimeOfDay states to Day,Evening, and Night. I also changed motion control to “pause” and added an “Override” mode as well. This way, if i want to “pause” the motion control from the switch i can do so BUT i can also completely override motion control via a switch on my sitemap. I intend to use this switch for functions like “party mode” or “Bedtime”. For example, you could tell Alexa “Goodnight” and have a routine that turns on this Override and whatever other stuff you might want the house to do at bedtime.

I learned a LOT in this exercise and I’ve still got plenty left to learn so any feedback/advice would be greatly appreciated.

I also want to thank @rlkoshak and @5iver ! You two gentlemen are of great value to this community!

2 Likes

I am new to this and came via the route of changing my alarm. I replaced the old alarm system with Konnected for my alarm so I could use all my existing PIR’s and Smoke detectors from my old alarm. As a result of this all the PIR’s are on my system now, so I put Sonoff minis behind my switches and hooked the two, so I can use the alarm PIR’s for my lights when the alarm is off.

I chose a slightly different logic to the original poster. My logic is follows;

I wanted the lights to work differently based on being used with the switch vs with the PIR. I wanted them to behave more normal with the switch, mostly because my wife is a little averse to home automation! :slight_smile: and just be quick and easy for PIR, so they work a little differently.

I have 2 timeouts, one if activated via the PIR, and one if activated via the switch. Both can me changed via the system, but have defaults of around 3 mins idle for the PIR and 30 mins for switch.

If you walk into a room and don’t hit the switch, the PIR will turn the light on, it will keep it on until it detects no movement for 3 mins, then turn off. while it keeps detecting movement it will obviously keep on. This is the kind of use where you just walk into a room to do something a while, then go out again.

If you walk in and turn the light on via the switch, it turns the light on with a longer timeout, of 30 mins. The use assumption being that if you turn it on you will prob be in there longer.

My wife found it very annoying that the light would go off and she needed to do something to turn it back on, and I can see the point, so having a far longer timeout via the switch makes it appear a more normal use of a switch

If you turn the light off and then on again at the switch within 2 seconds. this locks the light on and the PIR will then never turn it off.

If the PIR turns the light off and it sees movement it will turn it straight back on again.

However, if you turn the light off via the switch, the PIR it locked out for a peroid of time from turning the light back on. It is very annoying if you turn the light off, move in the room, and it comes back on again immediately. Another thing that made my wife want me to remove everything!! :slight_smile:

None of the automation kicks in at all, until the astro sunset is reached.

I think this seems to work pretty well and was nice use of the existing alarm system.

3 Likes

@boconnor just wanted to say thanks for posting this. I remember seeing your post a while back and it gave me some great ideas on how we could make automated lighting even better.

I really liked your idea of overriding motion control, so I incorporated it using zwave scenes and the expire binding to create a more simplified version of manual control. It’s working beautifully so far. By using scenes, you can have a much cleaner base of maintenance since you can get rid of proxy items and you can set the manual override time in a centralized location.

Not all zwave switches support scenes, but it’s becoming more and more common.

I’m glad I can help someone. I’m usually the one in desperate need of help around here.

I have some spare time over the next 8 weeks or so thanks to a pandemic induced lack of work.I’m actually hoping to get a chance to get back to the code for this and produce something polished and relatively bug-free. I’ll post it when I get a chance.

Your idea sounds good, but I found the expire binding to be too inflexible for my purposes. I use it in a few simple cases, but not being able to vary the duration of the timers ruined them for me. I’d love it if this binding could be extended somehow in the future. I also have never really gotten my head around the idea of scenes and how they work. I’m not sure whether the scene functionality on my Fibaro dimmers could be coaxed to work in this way. There does appear to be a scene number channel so maybe? I might have a play, but in the meantime I’m stuck with my complicated logic and proxies.

Those Inovelli switch/dimmers look very nice. And astonishingly cheap too. Z-wave home automation seems like a much cheaper hobby in the US than it does here.

Sorry to hear about your recent increase in availability. I guess the bright side is getting to play with OH.

Yes, I really love these devices. They’re a small company run by enthusiasts who really care about their products, so we’re lucky to have them. The CEO himself is active on reddit and other home automation discussion boards. I’ve tried zwave dimmers from a dozen different brands over the last 7 years or so, and these are my favorite so far.

Z-wave scenes are not well-documented outside of dedicated controllers, so I was in the same boat until I had some hands on time with some devices that offered them.

As it turns out, for switches and dimmers which support scenes (I’ve tried homeseer and inovelli), it’s pretty simple. The device sends an update to your zwave network based on action triggers defined by the manufacturer. For the OH zwave binding, this is received as a separate channel for the device.

In other words, if you single-tap up, the switch will report a status of “1.0” If you double-tap up, it will send out a “1.1”. If you double-tap down, it will send out “2.1”, and so on. You’ll have to look at the documentation for each device to see how many scenes they support since each device is different.

Scenes are perfect for this use case because we can now capture a hardware press without having to create extra proxy items.

If you link the model number, I can check the cd-jackson DB to see if your specific fibaro switch supports the scene channel for sending scene info the same way.

They’re FGD-212 dimmers. Looking through the manual it does seem like they can be configured to report scene information in the way you have described. I suspect I will be able to find a way to dramatically simplify my code (and possibly delete a huge pile of proxy items!), which will be fantastic.

My ‘free time’ starts in a few days so will work on it starting then.

I checked out the xml for the FGD212 and yes, it does appear it will support scenes in the zwave binding as well. The relevant channel is here:

  <channel id="scene_number" typeId="scene_number">
    <label>Scene Number</label>
    <properties>
      <property name="binding:*:DecimalType">COMMAND_CLASS_SCENE_ACTIVATION</property>
    </properties>
  </channel>

I’m sure you’ve already got this in mind, but here’s a friendly reminder just in case: before a full rewrite of your rules, make sure to check the performance of this zwave function to make sure it’s working to your standards.

You’ll probably want to test usability, accuracy, latency on a few switches first.

Don’t worry. It’ll all be done on a bench with a spare dimmer before I take it near anything already up and running and working.

Just wanted to let you know that I have sat down today and experimented with scenes in my dimmer. I wanted to report back that it works exactly as you have described with the Fibaro dimmers. I’ve only achieved proof of concept so far. It will be a few days (weeks?) until I have everything working the way I want it, but I wanted to say thanks straight away. This will definitely make my rules much easier to write and maintain.

1 Like

Fibaro scenes work really well. I have centre retractive switches for all my lights and they give a multitude of options when using scenes. I use the FGD-212’s alongside phillips hue bulbs (so i can always turn the light on if openhab or the network fail).

A few examples:

My Kitchen hue LED strips (on a lightwaverf plug socket to monitor energy etc) but able to turn on/off by a switch rather than app alone:

rule "Kitchen Top" when Item KitchenLightScene received update 26 then
if(AboveLight.state == OFF || AboveLight.state == NULL
{AboveLightColour.sendCommand("39,1,100")AboveLight.postUpdate(ON)}
else {AboveLight.postUpdate(OFF)AboveLightColour.sendCommand("39,1,0")}
end

rule "Kitchen Middle" when Item KitchenLightScene2 received update 26 then
if(BelowLight.state == OFF || BelowLight.state == NULL
BelowLight.postUpdate(ON)BelowLightColour.sendCommand("39,1,100")KitchenTVLights.sendCommand("39,1,100")}
else {BelowLight.postUpdate(OFF)BelowLightColour.sendCommand("39,1,0")KitchenTVLights.sendCommand("39,1,0")}
end

rule "Kitchen Bottom" when Item KitchenLightScene3 received update 26 then
if(PlynthLight.state == OFF || PlynthLightColour.state == NULL){PlynthLight.postUpdate(ON)PlynthLightColour.sendCommand("39,1,100")}
else {PlynthLight.postUpdate(OFF)PlynthLightColour.sendCommand("39,1,0")}
end

Then i have a scene activated by the switch for some nice party lights :slight_smile: :

var Timer PartyTimer = null

rule "Kitchen Party Mode" when Item KitchenLightScene received update 24 then
if(KitchenSpotLightSwitch.state == OFF){KitchenSpotLightSwitch.sendCommand(ON)}
if(PartyMode.state == OFF || PartyMode.state == NULL){PartyMode.sendCommand(ON)}
if(PartyMode.state == ON){PartyMode.sendCommand(OFF)}
end

rule "PartyMode On" when Item PartyMode changed to ON then
    if(PartyTimer !== null) return;
    PartyTimer = createTimer(now, [ |
        if(PartyMode.state == ON){
			KitchenSpotLightsColour?.members.forEach(
				KitchenSpotLightsColour|
					sendCommand(KitchenSpotLightsColour, ((((Math::random * 255).intValue)) + "," + 100 + "," + (((Math::random * 100).intValue))))
			)
		PartyTimer.reschedule(now.plusMillis(3000))}
        else {PartyTimer = null}
    ])
end

rule "Kitchen Party Mode OFF" when Item PartyMode changed from ON to OFF then
PartyTimer?.cancel
PartyTimer = null
KitchenSpotLightsColour?.members.forEach(KitchenSpotLightsColour|
		 sendCommand(KitchenSpotLightsColour,"39,1,100"))
KitchenScene.postUpdate(1)
end

I then have another that cycles through scenes (not finished updating them yet):

rule "Kitchen Scene" when Item KitchenLightScene received update 14 then
if(KitchenLight.state == OFF){KitchenLight.sendCommand(ON)}
if(KitchenScene.state == 1 || KitchenScene.state == NULL)
{KitchenSpotLightsColour.sendCommand("20,80,79")AboveLightColour.sendCommand("10,86,79")BelowLightColour.sendCommand("10,86,79")PlynthLightColour.sendCommand("10,86,79")KitchenTVLightsColour.sendCommand("10,86,79")KitchenScene.postUpdate(2)}

else if(KitchenScene.state == 2)
{KitchenSpotLightsColour.sendCommand("3,100,79")AboveLightColour.sendCommand("41,80,79")BelowLightColour.sendCommand("41,80,79")PlynthLightColour.sendCommand("41,80,79")KitchenTVLightsColour.sendCommand("41,80,79")KitchenScene.postUpdate(3)}

else if(KitchenScene.state == 3)
{KitchenSpotLightsColour.sendCommand("281,38,49")AboveLightColour.sendCommand("17,64,49")BelowLightColour.sendCommand("17,64,49")PlynthLightColour.sendCommand("41,80,79")KitchenTVLightsColour.sendCommand("17,64,49")KitchenScene.postUpdate(10)}

else if(KitchenScene.state == 4)
{KitchenSpotLightsColour.sendCommand("333,35,49")KitchenScene.postUpdate(5)}

else if(KitchenScene.state == 5)
{KitchenSpotLightsColour.sendCommand("20,83,49")KitchenScene.postUpdate(6)}

else if(KitchenScene.state == 6)
{KitchenSpotLightsColour.sendCommand("254,51,54")KitchenScene.postUpdate(7)}

else if(KitchenScene.state == 7)
{KitchenSpotLightsColour.sendCommand("197,100,54")KitchenScene.postUpdate(8)}

else if(KitchenScene.state == 8)
{KitchenSpotLightsColour.sendCommand("198,100,54")KitchenScene.postUpdate(9)}

else if(KitchenScene.state == 9)
{KitchenSpotLightsColour.sendCommand("261,100,54")KitchenScene.postUpdate(10)}

else if(KitchenScene.state == 10)
{KitchenSpotLightsColour.sendCommand("254,51,54")AboveLightColour.sendCommand("171,100,54")BelowLightColour.sendCommand("171,100,54")PlynthLightColour.sendCommand("171,100,54")KitchenTVLightsColour.sendCommand("198,100,54")KitchenScene.postUpdate(11)}

else if(KitchenScene.state == 11)
{KitchenSpotLightsColour.sendCommand("3,100,79")AboveLightColour.sendCommand("39,36,79")BelowLightColour.sendCommand("39,36,79")PlynthLightColour.sendCommand("39,36,79")KitchenTVLightsColour.sendCommand("10,86,79")KitchenScene.postUpdate(12)}

else if(KitchenScene.state == 12)
{KitchenSpotLightsColour.sendCommand("281,38,49")AboveLightColour.sendCommand("17,64,49")BelowLightColour.sendCommand("17,64,49")PlynthLightColour.sendCommand("17,64,49")KitchenTVLightsColour.sendCommand("281,38,60")KitchenScene.postUpdate(13)}

else if(KitchenScene.state == 13)
{KitchenSpotLightsColour.sendCommand("340,59,85")AboveLightColour.sendCommand("343,18,85")BelowLightColour.sendCommand("343,18,85")PlynthLightColour.sendCommand("343,18,85")KitchenTVLightsColour.sendCommand("342,43,85")KitchenScene.postUpdate(14)}

else if(KitchenScene.state == 14)
{KitchenSpotLightsColour.sendCommand("39,1,100")AboveLightColour.sendCommand("39,1,100")BelowLightColour.sendCommand("39,1,100")PlynthLightColour.sendCommand("39,1,100")KitchenTVLightsColour.sendCommand("39,1,100")KitchenScene.postUpdate(1)}

end
1 Like

Hi,
i am new at openhab and i really like the way it works

i only have question how you made the Bathroomlightvirtual ( is this a virtual device?)

See Design Pattern: Unbound Item (aka Virtual Item) and Design Pattern: Proxy Item

All you have to do is define the item in your .items file. It will then become an item in openHAB that functions just like any other dimmer (apart from not interacting with any real-world device because it doesn’t have a channel defined in the item).

okay… i understand now thanks for your quick reply