My openHAB-2 story

I was getting tired of my old Nexa Wireless 400MHz uni-directional system were I had several dimmers paired to a single wall-remote. Almost every time I wanted to turn the group on or off one or more of the dimmers failed to receive the signal. Had to move my body away from the signal path even though it was just 2m away. The WAF was falling too, so I had to do something …

I opted for Z-wave HW as the availability is great ‘over the counter’ in my area.
So, to decide what SW to put on my Shuttle XS35G Ubuntu box…?

Took a demo license of HS3 for a spin, but was not too exited with the GUI and the cost of the license.
After discussing this with a co-worker, he recommended openHAB and I have not looked back since.
This was 25. February 2017.

After installing Ubuntu Server 16.04 LTS on my Shuttle, I followed the setup tutorial and was up and running in less than a day. I ended up using Paper UI for including new Z-wave items, Eclipse SmartHome Designer for configuration, HABmin for troubleshooting and HABdroid for remote control.

Since I was already a bit Nexa-centric I started with a few Nexa nodes, the AeonTech USB Z-stick (gen5), 2 NodOn wall remotes and a few Aeon MultiSensor 6.

Using the Nexa nodes ruled out using openHAB 2.0 Stable as it has issues with the Alarm channel. It sort of worked, but the log was full of errors. Going snapshot fixed that.

So, I will attach my site-map, items and rules files as a starting point for other fellow beginners.
It will show examples of:

  • NodOn wall remote events
  • Movement detection events (with multiple OFF filter)
  • Time of day timer events (motor block heater ON MON-FRI)
  • Time of day rule filter (motion at night turning lights on dimmed)
  • Z-wave node groups, and group commands
  • Humidity level turning fan to MAX

Site MAP:

sitemap GTV7 label=“Me Casa”
{
Frame label=“Soner” {
Group item=groupSwitches label=“Soner Av/På” icon=“firstfloor”
}
Frame label=“Lys” {
Group item=livingRoom label=“Stua” icon=“firstfloor”
Group item=kitchen label=“Kjøkken” icon=“firstfloor”
Group item=diningRoom label=“Spisestue” icon=“firstfloor”
}

Frame label=“Annet” {
Group item=div label=“Diverse” icon=“settings”
Group item=lanItems label=“LAN” icon=“settings”
Group item=sensors label=“Sensorer” icon=“settings”
}
}

Items:

Group kitchen
Group livingRoom
Group diningRoom
Group switches
Group lights
Group sensors
Group remotes
Group div
Group lanItems
Group groupSwitches

Dimmer WallplugStuaCornerLamp “Lampe (sofa)” (livingRoom, lights) { channel = “zwave:device:3f5c8071:node13:switch_dimmer” }
Dimmer WallplugStuaShelfLamp “Lampett (hylle)” (livingRoom, lights) { channel = “zwave:device:3f5c8071:node10:switch_dimmer” }
Dimmer WallplugStuaWooferLamp “Lampett (woofer)” (livingRoom, lights) { channel = “zwave:device:3f5c8071:node8:switch_dimmer” }
Dimmer WallplugStuaPianoLamp “Pianolampe” (livingRoom, lights) { channel = “zwave:device:3f5c8071:node9:switch_dimmer” }
Dimmer BuildInStuaWindowLamps “Karnapp” (diningRoom, lights) { channel = “zwave:device:3f5c8071:node36:switch_dimmer” }
Dimmer BuildInKjDiningTableLamp “Pendel (spisebord)” (diningRoom, lights) { channel = “zwave:device:3f5c8071:node18:switch_dimmer” }
Dimmer WallplugKjCornerLamp “Lampe (spisestue)” (diningRoom, lights) { channel = “zwave:device:3f5c8071:node14:switch_dimmer” }

Dimmer BuildInKjRoofBenchLamp “Tak (kjøkkenbenk)” (kitchen, lights) { channel = “zwave:device:3f5c8071:node16:switch_dimmer” }
Dimmer BuildInKjRoofIslandLamp “Tak (kjøkkenøy)” (kitchen, lights) { channel = “zwave:device:3f5c8071:node17:switch_dimmer” }
Switch WallplugKjokkenPCBenchLamp “Benk (PCer)” (kitchen, lights) { channel = “zwave:device:3f5c8071:node3:switch_binary” }
Switch WallplugClosetLamp “Kott (trapp)” (div) { channel = “zwave:device:3f5c8071:node31:switch_binary” }
Switch BuildInKjokkenBench “Benk (overskap)” (kitchen, lights) { channel = “zwave:device:3f5c8071:node15:switch_binary” }

Dimmer BuildInMasterBathRoof “Tak (bad/oppe)” (div) { channel = “zwave:device:3f5c8071:node35:switch_dimmer” }
Switch BuildInMasterBathMirror “Speil (bad/oppe)” (div) { channel = “zwave:device:3f5c8071:node34:switch_binary” }

Switch BuildInWashingRoom “Tak (vaskerom)” (div) { channel = “zwave:device:3f5c8071:node32:switch_binary” }

Number ClosetTemperature “Temperatur (kott)” (sensors) { channel = “zwave:device:3f5c8071:node7:sensor_temperature” }
Number ClosetHumidity “Fuktighet (kott)” (sensors) { channel = “zwave:device:3f5c8071:node7:sensor_relhumidity” }
Switch ClosetMovement “Bevegelse (kott)” { channel = “zwave:device:3f5c8071:node7:sensor_binary” }

Number MasterBathTemperature “Temperatur (bad/oppe)” (sensors) { channel = “zwave:device:3f5c8071:node29:sensor_temperature” }
Number MasterBathHumidity “Fuktighet (bad/oppe)” (sensors) { channel = “zwave:device:3f5c8071:node29:sensor_relhumidity” }
Switch MasterBathMovement “Bevegelse (bad/oppe)” { channel = “zwave:device:3f5c8071:node29:sensor_binary” }

Number BathTemperature “Temperatur (bad/nede)” (sensors) { channel = “zwave:device:3f5c8071:node24:sensor_temperature” }
Number BathHumidity “Fuktighet (bad/nede)” (sensors) { channel = “zwave:device:3f5c8071:node24:sensor_relhumidity” }
Switch BathMovement “Bevegelse (bad/nede)” { channel = “zwave:device:3f5c8071:node24:sensor_binary” }

Number WashingRoomTemperature “Temperatur (vaskerom)” (sensors) { channel = “zwave:device:3f5c8071:node30:sensor_temperature” }
Number WashingRoomHumidity “Fuktighet (vaskerom)” (sensors) { channel = “zwave:device:3f5c8071:node30:sensor_relhumidity” }
Switch WashingRoomMovement “Bevegelse (vaskerom)” { channel = “zwave:device:3f5c8071:node30:sensor_binary” }

Switch WallplugHeaterLexus “Motorvarmer Lexus” (div, switches) { channel = “zwave:device:3f5c8071:node12:switch_binary” }
Switch WallplugReceiver “Receiver (loft)” (div, switches) { channel = “zwave:device:3f5c8071:node11:switch_binary” }
Switch WallplugTV “TV” (div, switches) { channel = “zwave:device:3f5c8071:node4:switch_binary” }
Switch FanS1 “Vifte av/1” (div, switches) { channel = “zwave:device:3f5c8071:node28:switch_binary” }
Switch FanS2 “Vifte 2” (div, switches) { channel = “zwave:device:3f5c8071:node26:switch_binary” }
Switch FanS3 “Vifte 3” (div, switches) { channel = “zwave:device:3f5c8071:node27:switch_binary” }

Number WallSwitchStua “Bryter (stua)” (div, remotes) { channel = “zwave:device:3f5c8071:node22:scene_number” }
Number WallSwitchKjokken “Bryter (kjøkken)” (div, remotes) { channel = “zwave:device:3f5c8071:node6:scene_number” }

Switch PhoneOMR “OMR on LAN” (lanItems) { channel = “network:device:192_168_1_50:online” }
Switch PhoneMDY “MDY on LAN” (lanItems) { channel = “network:device:192_168_1_51:online” }
Switch PhoneMJR “MJR on LAN” (lanItems) { channel = “network:device:192_168_1_52:online” }
Switch Onkyo “Receiver on LAN” (lanItems) { channel = “network:device:192_168_1_30:online” }

Switch All “Alle” (groupSwitches)
Switch AllLR “Stue” (groupSwitches)
Switch AllDR “Spisestue” (groupSwitches)
Switch AllK “Kjøkken” (groupSwitches)

String MotionTimeOn “Trappekott ON[%s]” (sensors)
String MotionTimeOff “Trappekott OFF[%s]” (sensors)
String MasterBathMotionTimeOn “Bad oppe ON[%s]” (sensors)
String MasterBathMotionTimeOff “Bad oppe OFF[%s]” (sensors)
String BathMotionTimeOn “Bad nede ON[%s]” (sensors)
String BathMotionTimeOff “Bad nede OFF[%s]” (sensors)
String WashingRoomMotionTimeOn “Vaskerom ON[%s]” (sensors)
String WashingRoomMotionTimeOff “Vaskerom OFF[%s]” (sensors)

Dimmer SqueezeBoxRadio “Radio (bad/oppe) [%.1f %%]” (div) { channel = “squeezebox:squeezeboxplayer:2C50A8B2-B856-484B-A862-344F14F36ED9:0004202641ce:volume” }

Wall-switch rules (aka remote)

//import java.time.LocalDateTime
import java.time.*

//import org.openhab.core.library.types.*


rule "All" 
when
        Item All received command
then 
		sendCommand(lights, if (All.state==ON) ON else OFF);
	    AllLR.state=All.state	    
	    AllK.state=All.state	    
	    AllDR.state=All.state	    
end

rule "AllLR" 
when
        Item AllLR received command
then 
		sendCommand(livingRoom, if (AllLR.state==ON) ON else OFF);
end

rule "AllDR" 
when
        Item AllDR received command
then 
		sendCommand(diningRoom, if (AllDR.state==ON) ON else OFF);
end

rule "AllK" 
when
        Item AllK received command
then 
		sendCommand(kitchen, if (AllK.state==ON) ON else OFF);
end


rule "WallSwitchLivingRoom" 
when
        Item WallSwitchStua received update
then 
    var swState = WallSwitchStua.state 
	logInfo("RemoteNodon22", "Rule: Got: " + swState)
	Thread::sleep(150)
    
    switch swState {
	    case 1.0: {logInfo("RemoteNodon22", "Rule: Button1 SingleClick " + swState) sendCommand(lights, if (BuildInKjokkenBench.state==OFF) ON else OFF);
	    	                                                                        lights.state = if (BuildInKjokkenBench.state==OFF) ON else OFF
                                                                                    logInfo("RemoteNodon22", "lights.state=" + lights.state)
	    																		    All.state=lights.state 
	    																		    AllLR.state=lights.state	    
	    																		    AllK.state=lights.state	    
	    																		    AllDR.state=lights.state	    
	    																		   }	    
	    case 1.1: {logInfo("RemoteNodon22", "Rule: Button1 Release " + swState)}
	    case 1.2: {logInfo("RemoteNodon22", "Rule: Button1 Hold-down " + swState)}
	    case 1.3: {logInfo("RemoteNodon22", "Rule: Button1 DoubleClick " + swState)}	    
	    case 2.0: {logInfo("RemoteNodon22", "Rule: Button2 SingleClick " + swState) sendCommand(livingRoom, if (WallplugStuaPianoLamp.state==0) ON else OFF);
	    	                                                                        livingRoom.state = if (WallplugStuaPianoLamp.state==OFF) ON else OFF
                                                                                    logInfo("RemoteNodon22", "livingRoom.state=" + lights.state)
	    																		    AllLR.state=livingRoom.state 
	    																		    }	    
	    case 2.1: {logInfo("RemoteNodon22", "Rule: Button2 Release " + swState)}
	    case 2.2: {logInfo("RemoteNodon22", "Rule: Button2 Hold-down " + swState)}
	    case 2.3: {logInfo("RemoteNodon22", "Rule: Button2 DoubleClick " + swState)}	    
	    case 3.0: {logInfo("RemoteNodon22", "Rule: Button3 SingleClick " + swState)}
	    case 3.1: {logInfo("RemoteNodon22", "Rule: Button3 Release " + swState)}
	    case 3.2: {logInfo("RemoteNodon22", "Rule: Button3 Hold-down " + swState)}
	    case 3.3: {logInfo("RemoteNodon22", "Rule: Button3 DoubleClick " + swState)}	    
	    case 4.0: {logInfo("RemoteNodon22", "Rule: Button4 SingleClick " + swState)
	    }
	    case 4.1: {logInfo("RemoteNodon22", "Rule: Button4 Release " + swState)}
	    case 4.2: {logInfo("RemoteNodon22", "Rule: Button4 Hold-down " + swState)}
	    case 4.3: {logInfo("RemoteNodon22", "Rule: Button4 DoubleClick " + swState)}	    
	    default:  {logInfo("RemoteNodon22", "Rule: Say what? " + swState)}
    }

end

rule "WallSwitchKitchen"
when
        Item WallSwitchKjokken received update
then
    var swState = WallSwitchKjokken.state 
	logInfo("RemoteNodon6", "Rule: Got: " + swState)
	Thread::sleep(150)

    switch swState {
	    case 1.0: {
	    	       logInfo("RemoteNodon6", "Rule: Button1 SingleClick " + swState) sendCommand(lights, if (BuildInKjokkenBench.state==OFF) ON else OFF)
	    	                                                                       lights.state = if (BuildInKjokkenBench.state==OFF) ON else OFF
                   																   logInfo("RemoteNodon6", "lights.state=" + lights.state)
	    																		   All.state=lights.state 
	    																		   AllLR.state=lights.state	    
	    																		   AllK.state=lights.state	    
	    																		   AllDR.state=lights.state	    
	    																		   }	    
	    case 1.1: {logInfo("RemoteNodon6", "Rule: Button1 Release " + swState)}
	    case 1.2: {logInfo("RemoteNodon6", "Rule: Button1 Hold-down " + swState)}
	    case 1.3: {logInfo("RemoteNodon6", "Rule: Button1 DoubleClick " + swState)}	    
	    case 2.0: {logInfo("RemoteNodon6", "Rule: Button2 SingleClick " + swState) sendCommand(kitchen, if (BuildInKjokkenBench.state==OFF) ON else OFF)
	    	                                                                       kitchen.state = if (BuildInKjokkenBench.state==OFF) ON else OFF
                   																   logInfo("RemoteNodon6", "kitchen.state=" + kitchen.state)
	    																		   AllK.state=kitchen.state 
	    																		   }	    
	    case 2.1: {logInfo("RemoteNodon6", "Rule: Button2 Release " + swState)}
	    case 2.2: {logInfo("RemoteNodon6", "Rule: Button2 Hold-down " + swState)}
	    case 2.3: {logInfo("RemoteNodon6", "Rule: Button2 DoubleClick " + swState)}	    
	    case 3.0: {logInfo("RemoteNodon6", "Rule: Button3 SingleClick " + swState)
                  																   logInfo("RemoteNodon6", "Date " + LocalDateTime.now())
										        		       MotionTimeOn.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")															        		       
//										        		       MotionTime.postUpdate("Heia")															        		       
	    }
	    case 3.1: {logInfo("RemoteNodon6", "Rule: Button3 Release " + swState)}
	    case 3.2: {logInfo("RemoteNodon6", "Rule: Button3 Hold-down " + swState)}
	    case 3.3: {logInfo("RemoteNodon6", "Rule: Button3 DoubleClick " + swState)}	    
	    case 4.0: {logInfo("RemoteNodon6", "Rule: Button4 SingleClick " + swState) sendCommand(diningRoom, if (BuildInKjDiningTableLamp.state==0) ON else OFF)
	    	                                                                       diningRoom.state = if (BuildInKjDiningTableLamp.state==0) ON else OFF
                   																   logInfo("RemoteNodon6", "BuildInKjDiningTableLamp.state=" + BuildInKjDiningTableLamp.state)
                   																   logInfo("RemoteNodon6", "diningRoom.state=" + diningRoom.state)
	    																		   AllDR.state=diningRoom.state
	    																		   }	    
	    case 4.1: {logInfo("RemoteNodon6", "Rule: Button4 Release " + swState)}
	    case 4.2: {logInfo("RemoteNodon6", "Rule: Button4 Hold-down " + swState)}
	    case 4.3: {logInfo("RemoteNodon6", "Rule: Button4 DoubleClick " + swState)}
	    default:  {logInfo("RemoteNodon6", "Rule: Say what? " + swState)}
    }	    

end

Motion rules:

import java.time.LocalTime
import java.time.LocalDate

    var boolean onLR = false
    var boolean onMBR = false
    var boolean onBR = false
    var boolean onVR = false
    val int masterBathDimlevelDay = 35
    val int masterBathDimlevelNight = 4
    val int masterBathVolumeDay = 25
    val int masterBathVolumeNight = 5
    val int dayStartHour = 07
    val int dayStartMin = 00
    val int dayEndHour = 23
    val int dayEndMin = 30

rule "MotionCloset"
when
        Item ClosetMovement received update
then

        logInfo("Motion", "ClosetMovement.state=" + ClosetMovement.state)
        if (ClosetMovement.state==ON) {
        	onLR = true        	
        	MotionTimeOn.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")        	
        	sendCommand(WallplugClosetLamp, ON)        	
        } else {
        	if (onLR) {
        		onLR = false
        		MotionTimeOff.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")        	
          	    sendCommand(WallplugClosetLamp, OFF)        	
        	}        	
        }
end

rule "MotionMasterBad"
when
        Item MasterBathMovement received update
then
        var LocalTime start = LocalTime.of( dayStartHour , dayStartMin );
        var LocalTime stop = LocalTime.of(  dayEndHour , dayEndMin );
        var Boolean isNowOnOrAfterStart = ( ! LocalTime.now().isBefore( start ) ) ;  // A briefer way of asking "is equal to OR is after" is "is not before". 
        var Boolean isNowBeforeStop = LocalTime.now().isBefore( stop );
        var Boolean isNowInTargetZone = ( isNowOnOrAfterStart && isNowBeforeStop ); // Half-Open: beginning is inclusive while ending is exclusive.
        
        
        logInfo("Motion", "MasterBathMovement.state=" + MasterBathMovement.state)
        if (MasterBathMovement.state==ON) {
        	onMBR = true
        	MasterBathMotionTimeOn.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")
        	if (isNowInTargetZone) {
                logInfo("Light", "Turning on Mirror light, dimming roof light to " + masterBathDimlevelDay + "%")        		
           	    sendCommand(BuildInMasterBathMirror, ON)        	
        	    sendCommand(BuildInMasterBathRoof, masterBathDimlevelDay)
                logInfo("Motion", "Increasing radio volume")
	        	sendCommand(SqueezeBoxRadio, masterBathVolumeDay)
                logInfo("Fan", "Fan speed2 ON")
	        	sendCommand(FanS2, ON)
        	} else {
           	    sendCommand(BuildInMasterBathRoof, masterBathDimlevelNight)        	
                logInfo("Motion", "It's night, do not increase radio volume")        		
                logInfo("Fan", "It's night, do not turn on fan")        		
                logInfo("Light", "It's night, No mirror light, dimming roof light to " + masterBathDimlevelNight + "%")        		
        	}
        } else {
        	if (onMBR) {
        		onMBR = false
        		MasterBathMotionTimeOff.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")        	
            	logInfo("Motion", "Muting radio volume")
        		sendCommand(SqueezeBoxRadio, masterBathVolumeNight)
                logInfo("Light", "Turning off lights")        		
            	sendCommand(BuildInMasterBathMirror, OFF)        	
            	sendCommand(BuildInMasterBathRoof, OFF)
        		if (!onMBR && !onBR && !onVR) {         	
            	    logInfo("Fan", "Fan speed2 OFF")
        		    sendCommand(FanS2, OFF)
        		} else {
            	    logInfo("Motion", "One of FAN controllers is still active, postponing switch-off")        			
        		}
        	}
        }
end

rule "MotionNedeBad"
when
        Item BathMovement received update
then
        var LocalTime start = LocalTime.of( dayStartHour , dayStartMin );
        var LocalTime stop = LocalTime.of(  dayEndHour , dayEndMin );
        var Boolean isNowOnOrAfterStart = ( ! LocalTime.now().isBefore( start ) ) ;  // A briefer way of asking "is equal to OR is after" is "is not before". 
        var Boolean isNowBeforeStop = LocalTime.now().isBefore( stop );
        var Boolean isNowInTargetZone = ( isNowOnOrAfterStart && isNowBeforeStop ); // Half-Open: beginning is inclusive while ending is exclusive.

        logInfo("Motion", "BathMovement.state=" + BathMovement.state)
        if (BathMovement.state==ON) {
        	onBR = true
        	BathMotionTimeOn.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")        	
        	if (isNowInTargetZone) {
                logInfo("Fan", "Fan speed2 ON")
	        	sendCommand(FanS2, ON)
        	} else {
                logInfo("Fan", "It's night, do not turn on fan")        		
        	}
        } else {
        	if (onBR) {
        		onBR = false
        		BathMotionTimeOff.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")        	
        		if (!onMBR && !onBR && !onVR) {         	
            	    logInfo("Fan", "Fan speed2 OFF")
        		    sendCommand(FanS2, OFF)
        		} else {
            	    logInfo("Motion", "One of FAN controllers is still active, postponing switch-off")        			
        		}
        	}
        }
end

rule "MotionVaskerom"
when
        Item WashingRoomMovement received update
then
        var LocalTime start = LocalTime.of( dayStartHour , dayStartMin );
        var LocalTime stop = LocalTime.of(  dayEndHour , dayEndMin );
        var Boolean isNowOnOrAfterStart = ( ! LocalTime.now().isBefore( start ) ) ;  // A briefer way of asking "is equal to OR is after" is "is not before". 
        var Boolean isNowBeforeStop = LocalTime.now().isBefore( stop );
        var Boolean isNowInTargetZone = ( isNowOnOrAfterStart && isNowBeforeStop ); // Half-Open: beginning is inclusive while ending is exclusive.

        logInfo("Motion", "WashingRoomMovement.state=" + WashingRoomMovement.state)
        if (WashingRoomMovement.state==ON) {
        	onVR = true
        	WashingRoomMotionTimeOn.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")
        	sendCommand(BuildInWashingRoom, ON)        	        	
        	if (isNowInTargetZone) {
                logInfo("Fan", "Fan speed2 ON")
	        	sendCommand(FanS2, ON)
        	} else {
                logInfo("Fan", "It's night, do not turn on fan")        		
        	}
        } else {
        	if (onVR) {
        		onVR = false
        		WashingRoomMotionTimeOff.postUpdate(LocalTime.now().toString() + " (" + LocalDate.now().toString() + ")")        	
            	sendCommand(BuildInWashingRoom, OFF)        	        	
        		if (!onMBR && !onBR && !onVR) {         	
            	    logInfo("Fan", "Fan speed2 OFF")
        		    sendCommand(FanS2, OFF)
        		} else {
            	    logInfo("Motion", "One of FAN controllers is still active, postponing switch-off")        			
        		}
        	}
        }
end

Timer rules:

//import org.openhab.core.library.types.*

rule “MotorvarmerOn”
when
Time cron “0 0 6 ? * MON-FRI *” // 04:00 ma-fr
// Time cron “0 0 8 ? * * *” // 07:00
then
sendCommand(WallplugHeaterLexus, ON)
end

rule “MotorvarmerOff”
when
Time cron “0 0 9 ? * * *” // Every day
then
sendCommand(WallplugHeaterLexus, OFF)
end

rule “ReceiverOff”
when
Time cron “0 0 3 ? * * *” // Every day
then
sendCommand(WallplugReceiver, OFF)
end

rule “ReceiverOn”
when
Time cron “0 0 6 ? * * *” // Every day
then
sendCommand(WallplugReceiver, ON)
end

And finally my Fan rules:

var boolean on = false

rule "FanS3" 
when
        Item MasterBathHumidity received update or 
        Item BathHumidity received update or 
        Item WashingRooomHumidity received update 
then 
		if ((MasterBathHumidity.state > 50) || (BathHumidity.state > 50) || (WashingRoomHumidity.state > 50)) {
           logInfo("Fan", "Fan speed3 ON")
  		   sendCommand(FanS3, ON);
  		   on = true;
		} else {
		if (on) {
  	       on = false
           logInfo("Fan", "Fan speed3 OFF")
	       sendCommand(FanS3, OFF);
	      }
	   }			
end

Note that the fan is controlled by both motion and humidity from 3 locations, 2 baths and a washing room.
There is logic so that a motion OFF signal will not turn the fan off too early if another room just started it’s motion ON period. Motion sensor OFF-ON period is set to 10min.

Note also, that I have no persistence, but every motion ON and OFF generates a text string variable timestamp items in a group that the sitemap shows.

Hope this can inspire others and maybe trigger some feedback on things I have gotten wrong :slight_smile:

5 Likes

Thanks for sharing!

Just for completeness I want to share with you the workaround to this issue:

The new .tems section and corresponding .sitemap section should be like this in order to gain full control of ordering:

.items

Number ClosetTemperature          (sensors)              { channel = "zwave:device:f180343d:node7:sensor_temperature" }
Number ClosetHumidity             (sensors)              { channel = "zwave:device:f180343d:node7:sensor_relhumidity" }
Switch ClosetMovement                                    { channel = "zwave:device:f180343d:node7:sensor_binary" }

Number MasterBathTemperature      (sensors)              { channel = "zwave:device:f180343d:node29:sensor_temperature" }
Number MasterBathHumidity         (sensors)              { channel = "zwave:device:f180343d:node29:sensor_relhumidity" }
Switch MasterBathMovement                                { channel = "zwave:device:f180343d:node29:sensor_binary" }

Number BathTemperature            (sensors)              { channel = "zwave:device:f180343d:node24:sensor_temperature" }
Number BathHumidity               (sensors)              { channel = "zwave:device:f180343d:node24:sensor_relhumidity" }
Switch BathMovement                                      { channel = "zwave:device:f180343d:node24:sensor_binary" }
Dimmer BuildInBathRoof            (div)                  { channel = "zwave:device:f180343d:node41:switch_dimmer" }

Number WashingRoomTemperature     (sensors)              { channel = "zwave:device:f180343d:node30:sensor_temperature" }
Number WashingRoomHumidity        (sensors)              { channel = "zwave:device:f180343d:node30:sensor_relhumidity" }
Switch WashingRoomMovement                               { channel = "zwave:device:f180343d:node30:sensor_binary" }

Number EntreTemperature           (sensors)              { channel = "zwave:device:f180343d:node38:sensor_temperature" }
Number EntreHumidity              (sensors)              { channel = "zwave:device:f180343d:node38:sensor_relhumidity" }
Switch EntreMovement                                     { channel = "zwave:device:f180343d:node38:sensor_binary" }


String MotionTimeOn               (sensors)
String MotionTimeOff              (sensors)
String MasterBathMotionTimeOn     (sensors)
String MasterBathMotionTimeOff    (sensors)
String BathMotionTimeOn           (sensors)
String BathMotionTimeOff          (sensors)
String WashingRoomMotionTimeOn    (sensors)
String WashingRoomMotionTimeOff   (sensors) 
String EntreMotionTimeOn          (sensors)
String EntreMotionTimeOff         (sensors) 

.sitemap

Text item=sensors label="Sensorer" icon="settings" {
  Frame {
  	Group item=sensors label="Temperaturer" icon="temperature" {
  	  Frame {
        Text item=EntreTemperature       label="Entré    [%.1f °C]"
        Text item=WashingRoomTemperature label="Vaskerom [%.1f °C]"
        Text item=BathTemperature        label="Bad nede [%.1f °C]"
        Text item=MasterBathTemperature  label="Bad oppe [%.1f °C]"
        Text item=ClosetTemperature      label="Kott     [%.1f °C]"
      }
    }

  	Group item=sensors label="Fuktighet" icon="humidity" {
  	  Frame {
        Text item=EntreHumidity       label="Entré    [%.1f %%]"
        Text item=WashingRoomHumidity label="Vaskerom [%.1f %%]"
        Text item=MasterBathHumidity  label="Bad oppe [%.1f %%]"
        Text item=BathHumidity        label="Bad nede [%.1f %%]"
        Text item=ClosetHumidity      label="Kott     [%.1f %%]"
      }
    }

  	Group item=sensors label="Bevegelser" icon="motion" {
  	  Frame {
        Text item=EntreMotionTimeOn        icon="motion" label="Entré ON[%s]"
        Text item=EntreMotionTimeOff       icon="motion" label="Entré OFF[%s]"
        Text item=WashingRoomMotionTimeOn  icon="motion" label="Vaskerom ON[%s]"
        Text item=WashingRoomMotionTimeOff icon="motion" label="Vaskerom OFF[%s]"
        Text item=MasterBathMotionTimeOn   icon="motion" label="Bad oppe ON[%s]"
        Text item=MasterBathMotionTimeOff  icon="motion" label="Bad oppe OFF[%s]"
        Text item=BathMotionTimeOn         icon="motion" label="Bad nede ON[%s]"
        Text item=BathMotionTimeOff        icon="motion" label="Bad nede OFF[%s]"
        Text item=MotionTimeOn             icon="motion" label="Kott ON[%s]"
        Text item=MotionTimeOff            icon="motion" label="Kott OFF[%s]"
      }
      }
      }
    }    

result:

Also, in the .sitemap, Dimmer becomes Slider:

Dimmer WallplugStuaPianoLamp      (livingRoom, lights)   { channel = "zwave:device:f180343d:node9:switch_dimmer" }
Switch BulbStairsState            (livingRoom, lights)   { channel = "ikeatradfri:whitespectrum_bulb:gwb072bf25aee1:65543:state" }
Dimmer BulbStairsBright           (livingRoom)           { channel = "ikeatradfri:whitespectrum_bulb:gwb072bf25aee1:65543:brightness" }

        Slider item=WallplugStuaPianoLamp      label="Pianolampe"              icon="dimmablelight"
        Switch item=BulbStairsState            label="Trapp (av/på)"           icon="light"
        Slider item=BulbStairsBright           label="Trapp (dimmer)"          icon="dimmablelight"

As you can see, openHAB also supports Ikea Tradfri. Search the forum and you will find :slight_smile:
One can use openHAB to overcome a current shortcoming in Tradfri. Each group of bulb or panel can only pair to one remote, so no stairway function is possible. So, if you have the Tradfri HUB and an always on PC on your LAN you can get group stairway function using 2 or more Ikea remotes or simply integrate with Z-Wave or other remotes of your choice.

1 Like