Introduction
This is the lighting tutorial (DESIGN PATTERN) which is a part of my tutorial list, I keep coming back to them, improving them add functionality to them etc, so if you like it then hit “like” it and you will get notified when changes occurs. I would like to thanks @rlkoshak for the help with this one.
Please see Design Pattern: What is a Design Pattern and How Do I Use Them for details on what a DP is and how to use them.
So for those who are used to phillips hue, IKEA, ewelink and so on should know that OH does not work in the same easy way. So if you buy a hue light bulb, you will plug it in, open the hue app, find the lamp bulb then give it a name, assign it to a room. Then you have a fully working lamp which you have dimmer widget for, a color change widget and a simple on off switch as well as it works with alexa.
So its unfortunately not so simple in Oh and hence this tutorial will help you to make the same functionality as the hue app, unfortunately you need to create the widget urself, but the logic behind the widget I have made for you.
Change Log
Please keep in mind that the tutorial is not quite finished and a few details might be missing. I decided to post it rather sooner than later. Comments are welcome!
!Update 22.11.2019
Updated the code for light switches
!Update 08.11.2019
Added some more background info about group files for dimmers, scenes
!Update 07.10.2019
Added some more background info as well as a default map file
!Update 20.07.2019
Fixed a bug in coloritem change
!Update 24.04.2019
Added the new rule that useses triggeringItem, this became available in OH2.3 and makes the rules way simpler
!Update 23.04.2019
Added Screenshot of current layout in Basic UI
!Update 22.02.2019
Initial Commit
Implementation in openhab
In this tutorial I would like to share my generic approach for lighting that might also be useful for others, its quite long so lets just jump into it.
We will discuss:
- Layout of room
- Types of lights
- Layout of light
- Light controllers
- Rules
- Light hardware
Layout of room
I have 6 rooms:
- Bathroom
- Bedroom
- MasterBedroom
- Livingroom
- Hallway
- Balcony
My layout can been seen below
Your layout will be different, but the important thing is not to call two rooms the same and have a naming pattern that is easy to understand! Also remember that everything is case sensitive so be consequent with the names!
User interface
So what we figured out over the years, is to keep the UI as simple as possible, how do we know? Well feedback from some over 60 differnt AirBnb people. So this is how our display looks like for every room:
However when implementing this into Openhab a great tip is to make an admin sitemap where you can test out different functionality that is not included WAF(Wife acceptance factor). So before you keep on reading make sure that each individual light work! Add the correct binding and test it out well before starting with this tutorial. So when your lights are working individually lets move on and do some magic and get them to work together!
Groups
So now you need to create a group for each room with your naming convection. The naming convetion is really important, pick one and stick to it. Its case sensitive so keep that in mind. I know its common to use gLivingRoom however with feedback from @rlkoshak I prefer to use Group_xxx so that it easily can be split later in the rules by “_”. For items, they all start with room name, so if you wnat use this DP, then follow same scheme otherwise you might have to modify rules to fit your naming convention
Group Group_Livingroom
Group Group_MasterBedroom
Group Group_Hallway
Group Group_Bedroom
Group Group_Bathroom
Group Group_Balcony
It is to be noted that you also needs some groups to keep your dimmers,scenes and so in:
Group Group_Scenes
Group Group_Dimmers
Group Group_Moods
Group Group_ColorPickers
Types of lights
In openhab we have 3 types of lights:
- Switch
- Dimmer
- Color
So you need to map your light to one of those types, an RGB light(Phillips hue) will then be mapped to a Color Item, a normal on off lamp will be mapped to Switch(SonOff MQTT) and a dimmable light will be dimmer(Zwave)
You can name your light items and map whatever binding to them as you like, my generic rule will handle them all. The important thing is that you add the room group and room scene(more on this later) it should belong to.
Layout of lights
When you build your own Taj Mahal you should think about how you should light it, and how you would use your place. A good light setting will make your home more welcome and relax and is something that is easy to forget when planning the home and is it hard to fix afterwards in a clean way.
So in my case I was not quite sure of how I would arrange furniture when I started the building progress so I ended up with a grid pattern of 120cm of the downlights(If I would start over again, I would probably change this slightly).
So now when you have the light plan ready, you should decide which light modes(Scenes) you would like. Here come the trick, you need to create a group for each light mode you would like EXCEPT OF: Every room needs to have at least ON/OFF of lights,( I personally also like to have NIGHT mode ). So go ahead and create light modes( such as reading, dinner, relax…) and decide which lights that should be activated in that mode,. When you have decided on these modes we need to create these groups :
Group Group_Livingroom_Lights_Night
Group Group_Livingroom_Lights_Dinner
Group Group_Livingroom_Lights_Wall
Group Group_Livingroom_Lights_FirePlace
Group Group_Livingroom_Lights_Kitchen
Group Group_Livingroom_Lights_TV
Group Group_Livingroom_Lights_Cooking
Group Group_Livingroom_Lights_Reading
Group Group_Livingroom_Lights_Lounge
and go back to your items(your lights) and add those groups to the item definition.
Color LL27 "LL27"(.....Group_LivingRoom,Group_LivingRoom_FirePlace,Group_LivingRoom_TV) {dmx="CHANNEL[55,56,57:100000]"}
You also need to make a map file and set it up in your sitemap, more about this later.
Light controllers
So I assume that you would like to control your light, there are several way to do so:
- Computer(Laptop,PC,Tablet)
- Physical device(Light switches, motion detectors,dimmers etc)
- Voice(Alexa, google,siri…)
Now comes the nice part(At least I think so…) For each room I have defined a scene selector(Even if the hallway only have one light(Simple switch)…)
So as mentioned above we created some groups representing the modes we liked to have in our home, so now is the time to implement this:
SCENES
So to actually turn on and off the lights in a ROOM we do not use a switch item but rather a number item which again will be set by selection item, physically press of a switch or voice command.
So in the lights.items files you need to create number item for each room and also this needs to belong to the Group Group_Scenes which will be used later in our generic rule:
Number Livingroom_Scene "Scene Selector"(Group_Scenes)
Number MasterBedroom_Scene "Scene Selector"(Group_Scenes)
Number Bathroom_Scene "Scene Selector"(Group_Scenes)
Number GuestBedroom_Scene "Scene Selector"(Group_Scenes)
Number Hallway_Scene "Scene Selector"(Group_Scenes)
Number Balcony_Scene "Scene Selector"(Group_Scenes)
these scene always have the same start mapping:
- 0=OFF
- 1=ON
- 2=NIGHT
- 3=…
- 4=…
so you need to manually fill in the names of your modes starting from 2, remember that for some rooms maybe just ON/OFF is OK, then you just add those. So in the sitemap the file would like this:
Selection item=Scene_Livingroom label="Scene Selector" mappings=[0="OFF", 1="ON", 2="AWAY, 3="DINNER", 4="READING",5="FIREPLACE", 6="BAR", 7="PARTY", 8="TV",9="COOKING", 10="WALL", 11="KITCHEN", 12="LOUNGE"]
For the physical devices such a single push button it might be hard to cycle through different scene, and these might therefore be better off just using as controlling all lights on/off in the given room. In my setup I did run 4 wires to each button locations so in theory I could swap single button to double or triple button, where button presses can be used as scene selector and/or dimming function. But for now I leave this as an optional feature for others to implement.
So for the physical devices add them to the room(Group) they should control as well as make a group for all light switches:
Group Group_LightSwitches
Each of these has at least one switch defined:
Contact Contact_BathroomL (Group_LightSwitches,Group_BathRoom) {gpio="pin:24 debounce:0 activelow:yes"}//SW3
Contact Contact_BathroomR (Group_LightSwitches,Group_BathRoom) {gpio="pin:25 debounce:0 activelow:yes"}//SW4
Presence sensor such as PIR motion sensor can be set up in the same way to control the lights.
I am only discussing alexa here, since that the system I am familiar with. Voice commands are in Openhab supported through the hue emulator or the skill. This means we need to create dummy items for each light mode, I have made an excel tool which can create all these items and corresponding rules for you or you can create them like this:
Switch Alexa_Livingroom_Reading "Reading" (Group_AlexaEmulator) [ "Lighting" ]
Switch Alexa_Livingroom_Dinner "Dinner" (Group_AlexaEmulator) [ "Lighting" ]
Switch Alexa_Livingroom_Tv "TV" (Group_AlexaEmulator) [ "Lighting" ]
Switch Alexa_Livingroom_Lounge "Relax" (Group_AlexaEmulator) [ "Lighting" ]
Switch Alexa_Livingroom_FirePlace "Fireplace" (Group_AlexaEmulator) [ "Lighting" ]
Switch Alexa_Livingroom_Cooking "Cooking" (Group_AlexaEmulator) [ "Lighting" ]
Switch Alexa_Livingroom_Kitchen "Kitchen" (Group_AlexaEmulator) [ "Lighting" ]
Switch Alexa_Livingroom_Party "Party" (Group_AlexaEmulator) [ "Lighting" ]
Switch Alexa_Livingroom_OnOff "Living Room" (Group_AlexaEmulator) [ "Lighting" ]
Note: Since the release of Alexa skill V3 we are supposed to be able to skip the virtual items and tag the groups directly instead, can someone please confirm this? If so we could make a rule and get the triggering item and then make a simple rule for voice command as well
Rules
Now the fun begins, we add some logic to our lights! So basically the idea is that you can add lights, change light modes and so on, but the rules always stay the same and does not need to be touched to get the system to work.
We will have 3 seperate rule files:
- LightControl.rules
- LightSwitches.rules
- AlexaEmulator.rules
LIGHT RULE
So we start out with the simple rule that that sends it ON/OFF then later we add the rule for dimming and changing colors(Only needed for those who has RGB lights, dimmable or those with wharm/cold white lights)
SCENE CHANGER
import org.eclipse.smarthome.model.script.ScriptServiceUtil
rule "Update lights"
when
Member of Group_Scenes changed
then
logInfo("Notification", "Scene changed: " + triggeringItem.name.split("_").get(0))
val roomName= triggeringItem.name.split("_").get(0) as String
val group =ScriptServiceUtil.getItemRegistry.getItem("Group_"+roomName +"_Lights") as GroupItem
val dimmer=ScriptServiceUtil.getItemRegistry.getItem(roomName +"_Dimmer") as DimmerItem
if(group === null) {
logError("admin", "Cannot find Item " + "Group_"+roomName +"_Lights")
return;
}
if (triggeringItem.state ==0) {
logInfo("Notification", "Group to set OFF: " + group.name)
group.members.forEach[ i | i.sendCommand(OFF) ]
if(dimmer !== null) {
dimmer.postUpdate(OFF)
}
}
else if (triggeringItem.state ==1) {
logInfo("Notification", "Group to set ON: " + group.name)
group.members.forEach[ i | i.sendCommand(ON) ]
if(dimmer !== null) {
dimmer.postUpdate(ON)
}
}
else{
val sceneGroup = ScriptServiceUtil.getItemRegistry.getItem( "Group_"+roomName +"_Lights_" +transform("MAP", roomName +".map", ""+triggeringItem.state) ) as GroupItem
logInfo("Notification", "Scene to set: " + sceneGroup.name)
if(sceneGroup === null) {
logError("admin", "Cannot find Item " + "Group_"+roomName +"_Lights_" +transform("MAP", roomName +".map", ""+triggeringItem.state))
return;
}
group.members.forEach[ i |
if (i.getGroupNames.contains(sceneGroup.name)){
i.sendCommand(ON)
}
else{
i.sendCommand(OFF)
}
]
}
end
DIMMER CHANGE
Then we have the case for those who has dimmable lights which might want to like to dimm the lights:
rule "Update dimmers"
when
Member of Group_Dimmers changed
then
logInfo("Notification", "Dimmer changed: " + triggeringItem.name.split("_").get(0))
val roomName= triggeringItem.name.split("_").get(0) as String
val group =ScriptServiceUtil.getItemRegistry.getItem("Group_"+roomName +"_Lights") as GroupItem
val dimmer=ScriptServiceUtil.getItemRegistry.getItem(roomName +"_Dimmer") as DimmerItem
if(group === null || dimmer === null) {
logError("admin", "Cannot find Item " +roomName +"_Dimmer or Group_"+roomName +"_Lights")
return;
}
group.members.forEach[ i |
// We need to grab active scene, transform it to group
val scene =ScriptServiceUtil.getItemRegistry.getItem(roomName +"_Scene") as NumberItem
//Just a nifty touch to update UI
//if (scene.state==0 ){
//scene.postUpdate(1)
//}
val sceneGroup = ScriptServiceUtil.getItemRegistry.getItem( "Group_"+roomName +"_Lights" +transform("MAP", roomName+ ".map", ""+scene.state) ) as GroupItem
// only dim lights if its a member of the selected group
if (i.getGroupNames.contains(sceneGroup.name)){
//Coloritems and dimmer items
if(i instanceof ColorItem || i instanceof DimmerItem) {
logInfo("Notification", "Icolor/dimmeritem: " + i.name)
i.sendCommand(dimmer.state)
}
//Switch item
else if(i instanceof SwitchItem) {
logInfo("Notification", "Iswitchitem: " + i.name)
// So normal lights get switched off when dim value passes 50%
if (dimmer.state<50){
i.sendCommand(OFF)
}
//it could have been dimmed below 50% and then dimmed back up again
else{
i.sendCommand(ON)
}
}
//In case someone added light group to non light item
else{
logError("admin", "item in wrong group " +i.name)
}
}
]
end
Color change
And finally we have those who would like to change colors of their lights depending on the mood, time of day etc…
Here its two method, one using a colorpicker other to use preselected colors. I am tempted to get away from the colorpicking, because I rarly use it. Also the preselected colors could be added to the scenes…
Color Picker
rule "Update color pickers"
when
Member of Group_ColorPickers changed
then
logInfo("Notification", "Color changed: " + triggeringItem.name.split("_").get(0))
val roomName= triggeringItem.name.split("_").get(0) as String
val group =ScriptServiceUtil.getItemRegistry.getItem("Group_"+roomName +"_Lights") as GroupItem
if(group === null ) {
logError("admin", "Cannot find Item Group_"+roomName +"_Lights")
return;
}
group.members.forEach[ i |
// We need to grab active scene, transform it to group
val scene =ScriptServiceUtil.getItemRegistry.getItem(roomName +"_Scene") as NumberItem
//Just a nifty touch to update UI
//if (scene.state==0 ){
//scene.postUpdate(1)
//}
val sceneGroup = ScriptServiceUtil.getItemRegistry.getItem( "Group_"+roomName +"_Lights" +transform("MAP", roomName+ ".map", ""+scene.state) ) as GroupItem
// only dim lights if its a member of the selected group
if (i.getGroupNames.contains(sceneGroup.name)){
//Coloritems
if(i instanceof ColorItem) {
logInfo("Notification", "Icolor/dimmeritem: " + i.name)
i.sendCommand(triggeringItem.state.toString)
}
}
]
end
Mood Selector
rule "Update mood"
when
Member of Group_Moods changed
then
logInfo("Notification", "Color changed: " + triggeringItem.name.split("_").get(0))
val roomName= triggeringItem.name.split("_").get(0) as String
val group =ScriptServiceUtil.getItemRegistry.getItem("Group_"+roomName +"_Lights") as GroupItem
if(group === null ) {
logError("admin", "Cannot find Item Group_"+roomName +"_Lights")
return;
}
group.members.forEach[ i |
// We need to grab active scene, transform it to group
val scene =ScriptServiceUtil.getItemRegistry.getItem(roomName +"_Scene") as NumberItem
//Just a nifty touch to update UI
//if (scene.state==0 ){
//scene.postUpdate(1)
//}
val sceneGroup = ScriptServiceUtil.getItemRegistry.getItem( "Group_"+roomName +"_Lights" +transform("MAP", roomName+ ".map", ""+scene.state) ) as GroupItem
// only dim lights if its a member of the selected group
if (i.getGroupNames.contains(sceneGroup.name)){
//Coloritems
if(i instanceof ColorItem) {
logInfo("Notification", "Colortem: " + transform("MAP", "Moods.map", ""+triggeringItem.state))
i.sendCommand(transform("MAP", "Moods.map", ""+triggeringItem.state))
}
}
]
end
Rules for physically devices
So lets move on to the lightSwitch(Add motion sensor as same as physically switch) … Note that I have commented out the option to have special scene called night being triggered when you press the switched if the time of day is night.
rule "Switch pressed"
when
Member of Group_LightSwitches changed from OPEN to CLOSED
then
var roomName = triggeringItem.name.split("_").get(0)
logInfo("Light switch", "room is now " + roomName)
// Well we need to be home if someone pressed a physical device
//Presence_Home.sendCommand(ON)
var sceneItem =ScriptServiceUtil.getItemRegistry.getItem(roomName +"_Scene") as NumberItem
logInfo("Light switch", "Scene is now " + sceneItem.name)
if (sceneItem!==null){
if(sceneItem.state == 0){
//if (vTimeOfDay.state.toString.contains("NIGHT")){
//sceneItem.sendCommand(2)
//}
//else{
sceneItem.sendCommand(1)
//}
}
else {
sceneItem.sendCommand(0)
}
}
end
VOICE COMMANDS
And finally the voice command
rule "Switch pressed"
when
Item Group_AlexaEmulator received command
then
Thread::sleep(5)
Presence_Home.sendCommand(ON)
val changedSwitch = Group_AlexaEmulator.members.filter[alexaItem | alexaItem.lastUpdate != null].sortBy[lastUpdate].last
if(alexaItem == null) logWarn("FILE", "Something is amiss, no Item has a valid lastUpdate!")
val sceneItemTemp = changedSwitch.name.toUpperCase().replace("ALEXA","SCENE") as String
val sceneItem = Group_RoomScenes.members.findFirst[roomName | sceneItemTemp.contains(roomName.name.toUpperCase())] as NumberItem
// Extract the SceneMode from SceneItemTemp
val sceneMode =
logInfo("Light switch", "Scene is now " + sceneItem)
if (sceneItem!=null){
if(sceneItem.state == 0){
if (vTimeOfDay.state.toString.contains("NIGHT")){
sceneItem.sendCommand(2)
}
else{
sceneItem.sendCommand(1)
}
}
else {
sceneItem.sendCommand(0)
}
}
end
Requirements
- You need to have the Map transformation to get this to work
- You need to install the Alexa skill to get voice command to work
- Optionally have time of day rule
Summary
- Install your lights
- Create groups, Each room and each scene beyond default
- Create a default map.file and scene.map file if needed
- Copy SceneChanger.rule
- Copy Switch/MotionSensor.rule
- If dimmeable light, copy Dimmer.rule
- If RGB led, copy Color.rule and Mood.rule
- If voice command copy Alexa.rule
- If more fancy actions copy timeOfDay.rules
Common bugs
- UI does not get updated to ON if scene is OFF and you select dimmer to dimm lights to ON
- Changing colors does not set Dimmer value in UI
- No error checking if map files exist, plan to implement a default one if non exist
- Can not define mood colors for scene preset, would like to do this in map file somehow
- Openhab can not use map file in sitemaps
- Autogeneration of selection item for sitemap and virtual items for voice assistance based on javascript from map files does not exist
Further readings
You are always welcome to my github for a more in depth explanation and easier cloning of files:
https://github.com/skatun/LittleChina/wiki or more specific:
- https://github.com/skatun/LittleChina/blob/master/Code/openHAB-conf/items/lights.items
- https://github.com/skatun/LittleChina/blob/master/Code/openHAB-conf/items/dmx.items
- https://github.com/skatun/LittleChina/blob/master/Code/openHAB-conf/items/contacts.items
- https://github.com/skatun/LittleChina/blob/master/Code/openHAB-conf/rules/light.rules
- https://github.com/skatun/LittleChina/tree/master/Code/openHAB-conf/transform
- https://github.com/skatun/LittleChina/blob/master/Code/openHAB-conf/sitemaps/littlechina.sitemap
- https://github.com/skatun/LittleChina/blob/master/Code/openHAB-conf/rules/timeOfDay.rules
Related Design Patterns(Link to Rich’s DP for now)
Design Pattern | How Used |
---|---|
Design Pattern: How to Structure a Rule | Overall structure of the first complex rule follows this DP |
Assocaited Items | Gets the Alerted Item based on the Online Item’s name |
Working with Groups in Rules | Get the Items around the time the rule was triggered, loop through those Items |
Time of Day | Time of the day to alter lights |
Heating | How to control the temperature in your Taj Mahal |