For who want to have ability to adjust configuration of scenarios in “runtime”, from the app, here you will find a working example of it.
This is a stand-alone example, you have obviously to modify something to integrate it in your system.
First of all, light items, for example lights.items.
// LIGHTS ITEMS & GROUPS
Group gLights
Switch Room1_Light01 "Light 01" (gLights)
Dimmer Room2_Light02 "Light 02" (gLights)
Switch Room3_Light03 "Light 03 RGB" (gLights)
Color Room3_Light03Color "Light 03 Color" (gLights)
Dimmer Room3_Light03Dim "Light 03 Intensity" (gLights)
Dimmer Room3_Light03Temp "Light 03 Color Temp" (gLights)
Note that every lights item have name of room included in its name: this is usefull for example to ignore some rooms when save or apply a scene
Then, scene configuration items (for example scenesconfig.items). Every light item must have corresponding config item of same type. In this example every configuration item is named whit prefix “CNF_SCENE_” added to light item name.
//----------------------------------------------------------------------------------------------
// SCENE CONFIG ITEM & GROUPS
Group gSceneConfig
Group gScenePersist
// Enable Scene Edit
Switch CNF_SCENE_EditMode "Edit Mode" <mobile>
// Number of Scene to Edit
Number CNF_SCENE_EditingSceneNum "[MAP(scenemap.map):%s]" (gScenePersist)
// Light Configuration Items
Switch CNF_SCENE_Room1_Light01 "Light 01" (gSceneConfig)
Dimmer CNF_SCENE_Room2_Light02 "Light 02" (gSceneConfig)
Switch CNF_SCENE_Room3_Light03 "Light 03 RGB" (gSceneConfig)
Color CNF_SCENE_Room3_Light03Color "Light 03 Color" (gSceneConfig)
Dimmer CNF_SCENE_Room3_Light03Dim "Light 03 Intensity" (gSceneConfig)
Dimmer CNF_SCENE_Room3_Light03Temp "Light 03 Color Temp" (gSceneConfig)
// Items for managing actions
Number CNF_SCENE_Action
String CNF_SCENE_Result "[%s]" <graph11>
Switch CNF_SCENE_ResultReset
// items that will store scene configuration
Group gScenes
String CNF_SCENE_Scene1 (gScenes, gScenePersist)
String CNF_SCENE_Scene2 (gScenes, gScenePersist)
String CNF_SCENE_Scene3 (gScenes, gScenePersist)
String CNF_SCENE_Scene4 (gScenes, gScenePersist)
String CNF_SCENE_Scene5 (gScenes, gScenePersist)
// If ON all the relative room lights are ignored during scene configuration contruction
// (So, when you will apply the scene no status of that room lights will be changed )
Group gSceneRoomIgnore
Switch CNF_SCENE_Ignore_ROOM1 "Ignore ROOM 1" <impostazione> (gSceneRoomIgnore)
Switch CNF_SCENE_Ignore_ROOM2 "Ignore ROOM 2" <impostazione> (gSceneRoomIgnore)
Switch CNF_SCENE_Ignore_ROOM3 "Ignore ROOM 3" <impostazione> (gSceneRoomIgnore)
Switch CNF_SCENE_VoiceConfirm "Vocal Confirm"
Then, rules to manage everything (for example scenes.rules):
import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import org.openhab.core.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.openhab.core.items.*
import java.util.regex.*
import java.util.String
import java.util.Calendar
import java.util.Date
import java.util.HashMap
import java.util.LinkedHashMap
import java.util.ArrayList
import java.util.Map
import org.eclipse.xtext.xbase.lib.*
// CONSTANTS
var String SCENE_ITEM_BASE_NAME = "CNF_SCENE_Scene"
var String CONFIG_ITEM_PREFIX = "CNF_SCENE_"
var String ROOM_IGNORE_PREFIX = "CNF_SCENE_Ignore_"
var String ITEM_SEPARATOR = ";"
var String INFO_SEPARATOR = ":"
var int ACTION_RESET_CONF = 2
var int ACTION_SAVE_CONF = 3
var int ACTION_SAVE_ACTUAL_STATUS = 4
var int ACTION_SCENE_TEST = 5
var String stempConfigStr = ""
var String sItemID = ""
var String sSceneItemName = ""
var String itemClassName = ""
var boolean bIgnoreItem = false
var String sSceneConf = ""
var String sMsgToSay = ""
var Timer timerResetStatus = null
var java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock()
// System Started Rule: Initialize some items
rule "System Started"
when
System started
then
CNF_SCENE_EditMode.postUpdate(OFF)
CNF_SCENE_Action.postUpdate(0)
CNF_SCENE_EditingSceneNum.postUpdate(0)
CNF_SCENE_Ignore_ROOM1.postUpdate(OFF)
CNF_SCENE_Ignore_ROOM2.postUpdate(OFF)
CNF_SCENE_Ignore_ROOM3.postUpdate(OFF)
end
rule "SCENE Configuration"
when
Item CNF_SCENE_Action received command
then
//lock.lock()
//try {
logInfo("SCENE","---> SCENE RULE STARTED <-----")
stempConfigStr = ""
switch (CNF_SCENE_Action.state) {
//--------------------------------------------------------------------------
// RESET ACTUAL CONFIGURATION
case ACTION_RESET_CONF: {
logInfo("SCENE","Action Received:" + "ACTION_RESET_CONF")
gSceneConfig?.members.forEach[configItem|
itemClassName = configItem.getClass().getName()
logInfo("SCENE","--configItem (" + configItem.name + ") is a = (" + itemClassName + ")")
//if (configItem instanceof SwitchItem) {
if (itemClassName.contains("SwitchItem")) {
logInfo("SCENE","SWITCH_ITEM--ItemNAME (" + configItem.name + ")")
configItem.sendCommand(OFF)
}
//else if (configItem instanceof DimmerItem){
else if (itemClassName.contains("DimmerItem")) {
logInfo("SCENE","DIMMER_ITEM--ItemNAME (" + configItem.name + ")")
configItem.sendCommand(0)
}
//else if (configItem instanceof ColorItem){
else if (itemClassName.contains("ColorItem")) {
logInfo("SCENE","COLOR_ITEM--ItemNAME (" + configItem.name + ")")
configItem.sendCommand("0.0,0.0,0.0")
}
]
stempConfigStr = ""
sSceneItemName = SCENE_ITEM_BASE_NAME + CNF_SCENE_EditingSceneNum.state.toString
gScenes?.allMembers.filter(s | s.name.contains(sSceneItemName)).forEach[sceneItem|
sendCommand(sceneItem as StringItem, stempConfigStr)
]
// Update status
CNF_SCENE_Result.postUpdate("Scene Resetted")
CNF_SCENE_ResultReset.sendCommand(ON)
}
//--------------------------------------------------------------------------
// SAVE ACTUAL CONFIGURATION TO SELECTED SCENE
case ACTION_SAVE_CONF: {
gSceneConfig?.members.forEach[configItem|
//logInfo("SCENE","Item Name = " + configItem.name)
sItemID = configItem.name.replace(CONFIG_ITEM_PREFIX,'')
itemClassName = configItem.getClass().getName()
if (configItem.state.toString=="Uninitialized") {
//if (configItem instanceof SwitchItem) {
if (itemClassName.contains("SwitchItem")) {
configItem.sendCommand(OFF)
}
//else if (configItem instanceof DimmerItem){
else if (itemClassName.contains("DimmerItem")) {
configItem.sendCommand(0)
}
//else if (configItem instanceof ColorItem){
else if (itemClassName.contains("ColorItem")) {
configItem.sendCommand("0.0,0.0,0.0")
}
}
if (stempConfigStr!="") {
stempConfigStr = stempConfigStr + ITEM_SEPARATOR
}
stempConfigStr = stempConfigStr + sItemID + INFO_SEPARATOR + configItem.state.toString
]
sSceneItemName = SCENE_ITEM_BASE_NAME + CNF_SCENE_EditingSceneNum.state.toString
gScenes?.allMembers.filter(s | s.name.contains(sSceneItemName)).forEach[sceneItem|
sendCommand(sceneItem as StringItem, stempConfigStr)
]
// Update status
CNF_SCENE_Result.postUpdate("Scene Saved")
CNF_SCENE_ResultReset.sendCommand(ON)
}
//--------------------------------------------------------------------------
// SAVE ACTUAL LIGHTS STATUS TO SELECTED SCENE
case ACTION_SAVE_ACTUAL_STATUS: {
logInfo("SCENE","---> REQUIRED ACTION = [ACTION_SAVE_ACTUAL_STATUS]")
gLights?.members.forEach[lightItem|
// Check if lights belong to a room that we want to ignore
bIgnoreItem = false
gSceneRoomIgnore.members.filter(s|s.state==ON).forEach[roomItem|
if (lightItem.name.lowerCase.contains(roomItem.name.replace(ROOM_IGNORE_PREFIX,'').lowerCase)) {
bIgnoreItem = true
}
]
// If it's not to be ignored..
if (!bIgnoreItem) {
sItemID = lightItem.name
itemClassName = lightItem.getClass().getName()
// check have valid state
if (lightItem.state.toString=="Uninitialized") {
//if (lightItem instanceof SwitchItem) {
if (itemClassName.contains("SwitchItem")) {
lightItem.sendCommand(OFF)
}
//else if (lightItem instanceof DimmerItem){
else if (itemClassName.contains("DimmerItem")) {
lightItem.sendCommand(0)
}
//else if (lightItem instanceof ColorItem){
else if (itemClassName.contains("ColorItem")) {
lightItem.sendCommand("0.0,0.0,0.0")
}
}
if (stempConfigStr!="") {
stempConfigStr = stempConfigStr + ITEM_SEPARATOR
}
stempConfigStr = stempConfigStr + sItemID + INFO_SEPARATOR + lightItem.state.toString
}
]
sSceneItemName = SCENE_ITEM_BASE_NAME + CNF_SCENE_EditingSceneNum.state.toString
gScenes?.allMembers.filter(s | s.name.contains(sSceneItemName)).forEach[sceneItem|
sendCommand(sceneItem as StringItem, stempConfigStr)
]
// Write on the configuration item status of each light just saved
var arrItems = stempConfigStr.split(ITEM_SEPARATOR)
for (i : arrItems) {
val itemConfString = i.split(INFO_SEPARATOR)
val configItemName = CONFIG_ITEM_PREFIX + itemConfString.get(0)
gSceneConfig?.allMembers.filter(s | s.name==configItemName).forEach[configItem|
configItem.sendCommand(itemConfString.get(1))
]
}
// Update status
CNF_SCENE_Result.postUpdate("Scene Saved")
CNF_SCENE_ResultReset.sendCommand(ON)
}
//--------------------------------------------------------------------------
// TEST SCENE
case ACTION_SCENE_TEST: {
if (CNF_SCENE_EditingSceneNum.state>0) {
CNF_SCENE_Result.postUpdate("Start Testing...")
// get config string from scene item
sSceneItemName = SCENE_ITEM_BASE_NAME + CNF_SCENE_EditingSceneNum.state.toString
gScenes?.allMembers.filter(s | s.name.contains(sSceneItemName)).forEach[sceneItem|
sSceneConf = sceneItem.state.toString
]
logInfo("SCENE","Saved Scene Config:" + sSceneConf)
if (sSceneConf!="Uninitialized" && sSceneConf!="") {
var arrItems = sSceneConf.split(ITEM_SEPARATOR)
for (i : arrItems) {
val itemConf = i.split(INFO_SEPARATOR)
val itemName = itemConf.get(0)
gLights?.allMembers.filter(s | s.name==itemName).forEach[lightItem|
//logInfo("SCENE","|||--->Item (" + configItem.name + ")--command (" + itemConf.get(1) + ")----")
lightItem.sendCommand(itemConf.get(1))
//Thread::sleep(200)
]
}
sMsgToSay = "Ok!"
say(sMsgToSay)
} else {
logInfo("SCENE","ATTENZIONE: SCENE NOT CONFIGURED YET")
sMsgToSay = "Scenario " + CNF_SCENE_EditingSceneNum.state + "non ancora configurato"
say(sMsgToSay)
}
}
// Update status
CNF_SCENE_Result.postUpdate("Ok, scene Applied")
CNF_SCENE_ResultReset.sendCommand(ON)
}
}
CNF_SCENE_Action.postUpdate(0)
logInfo("SCENE","---> SCENE RULE COMPLETED <-----")
//lock.unlock()
/*
}
catch(e){
logInfo("SCENE","--->RULE ERROR" + e.toString)
}
finally{
//lock.unlock()
}
*/
end
rule "SCENE Selection Change"
when
Item CNF_SCENE_EditingSceneNum changed
then
sSceneConf = ""
//lock.lock()
// try {
logInfo("SCENE","---------------------------------------------------------------------------")
// Selected Scene Changed
if (CNF_SCENE_EditingSceneNum.state>0) {
// Selected Scene Changed, get config string from scene item
sSceneItemName = SCENE_ITEM_BASE_NAME + CNF_SCENE_EditingSceneNum.state.toString
gScenes?.allMembers.filter(s | s.name.contains(sSceneItemName)).forEach[sceneItem|
sSceneConf = sceneItem.state.toString
]
logInfo("SCENE","Saved Scene Config:" + sSceneConf)
if (sSceneConf!="Uninitialized" && sSceneConf!="") {
var arrItems = sSceneConf.split(ITEM_SEPARATOR)
for (i : arrItems) {
val itemConf = i.split(INFO_SEPARATOR)
val configItemName = CONFIG_ITEM_PREFIX + itemConf.get(0)
gSceneConfig?.allMembers.filter(s | s.name==configItemName).forEach[configItem|
//logInfo("SCENE","|||--->Item (" + configItem.name + ")--command (" + itemConf.get(1) + ")----")
configItem.sendCommand(itemConf.get(1))
]
}
} else {
gSceneConfig?.members.forEach[configItem|
itemClassName = configItem.getClass().getName()
//if (configItem instanceof SwitchItem) {
if (itemClassName.contains("SwitchItem")) {
configItem.sendCommand(OFF)
}
//else if (configItem instanceof DimmerItem){
else if (itemClassName.contains("DimmerItem")) {
configItem.sendCommand(0)
}
//else if (configItem instanceof ColorItem){
else if (itemClassName.contains("ColorItem")) {
logInfo("SCENE","COLOR_ITEM--ItemNAME (" + configItem.name + ")")
if (configItem!=null) {
configItem.sendCommand("0.0,0.0,0.0")
}
}
]
}
}
/*
}
catch(e){
logInfo("SCENE","--->RULE ERROR" + e.toString)
}
finally{
lock.unlock()
} */
end
rule "SCENE Edit Status Reset"
when
Item CNF_SCENE_ResultReset received command ON
then
if (timerResetStatus == null) {
timerResetStatus = createTimer(now.plusSeconds(5)) [|
CNF_SCENE_Result.postUpdate("-")
timerResetStatus = null
]
} else {
timerResetStatus.reschedule(now.plusSeconds(5))
}
CNF_SCENE_ResultReset.sendCommand(OFF)
end
rule "SCENE Edit Mode Activation Change"
when
Item CNF_SCENE_EditMode received command
then
if (CNF_SCENE_EditMode.state==OFF) {
CNF_SCENE_EditingSceneNum.sendCommand(0)
}
end
Just a comment to the code, I don’t know why but this is not working for me:
if (configItem instanceof SwitchItem) {
so I had to use instead:
itemClassName = lightItem.getClass().getName()
if (itemClassName.contains("SwitchItem")) {
Then we need a transform file (scenesmap.map) for visualization on UI of custom scene names
0=Select scene
1=Scene 01
2=Scene 02
3=Scene 03
4=Scene 04
5=Scene 05
undefined=Select scene
-=Select scene
Persistence (for example rrd4j.persist):
Strategies {
everyMinute : "0 * * * * ?"
everyHour : "0 0 * * * ?"
everyDay : "0 0 0 * * ?"
// if no strategy is specified for an item entry below, the default list will be used
default = everyChange
}
Items {
gScenePersist* : strategy = everyChange, everyMinute, restoreOnStartup
}
Finally, sitemap (for example scene.sitemap):
sitemap Scene label="Scene"
{
Text label="Scene Config" icon="settings1" {
Switch item=CNF_SCENE_EditMode
Text item=CNF_SCENE_EditingSceneNum icon="azioni" visibility=[CNF_SCENE_EditMode==ON] valuecolor=[CNF_SCENE_EditingSceneNum=="Uninitialized"="red",CNF_SCENE_EditingSceneNum==0="red",CNF_SCENE_EditingSceneNum>0="green"] {
Switch item=CNF_SCENE_EditingSceneNum icon="azioni" label="Scene 01" mappings=[1="Select"] labelcolor=[CNF_SCENE_EditingSceneNum==1="green"]
Switch item=CNF_SCENE_EditingSceneNum icon="azioni" label="Scene 02" mappings=[2="Select"] labelcolor=[CNF_SCENE_EditingSceneNum==2="green"]
Switch item=CNF_SCENE_EditingSceneNum icon="azioni" label="Scene 03" mappings=[3="Select"] labelcolor=[CNF_SCENE_EditingSceneNum==3="green"]
Switch item=CNF_SCENE_EditingSceneNum icon="azioni" label="Scene 04" mappings=[4="Select"] labelcolor=[CNF_SCENE_EditingSceneNum==4="green"]
Switch item=CNF_SCENE_EditingSceneNum icon="azioni" label="Scene 05" mappings=[5="Select"] labelcolor=[CNF_SCENE_EditingSceneNum==5="green"]
}
Text label="Configure" icon="settings1" visibility=[CNF_SCENE_EditMode==ON] {
Switch item=CNF_SCENE_Room1_Light01
Slider item=CNF_SCENE_Room2_Light02
Switch item=CNF_SCENE_Room3_Light03
Colorpicker item=CNF_SCENE_Room3_Light03Color
Slider item=CNF_SCENE_Room3_Light03Dim
Slider item=CNF_SCENE_Room3_Light03Temp
Text label="Other"
Switch item=CNF_SCENE_Ignore_ROOM1
Switch item=CNF_SCENE_Ignore_ROOM2
Switch item=CNF_SCENE_Ignore_ROOM3
Switch item=CNF_SCENE_VoiceConfirm
}
Text label="Lights Status" icon="statoluci" {
Switch item=Room1_Light01
Slider item=Room2_Light02
Switch item=Room3_Light03
Colorpicker item=Room3_Light03Color
Slider item=Room3_Light03Dim
Slider item=Room3_Light03Temp
}
Text item=CNF_SCENE_Result
Switch item=CNF_SCENE_Action label="-" labelcolor=["white"] icon="azioni" mappings=[2="Cancel",1=" ",3="SAVE"] visibility=[CNF_SCENE_EditMode==ON]
Switch item=CNF_SCENE_Action label="-" labelcolor=["white"] icon="azioni" mappings=[4="LEARN"] visibility=[CNF_SCENE_EditMode==ON]
Switch item=CNF_SCENE_Action label="-" labelcolor=["white"] icon="azioni" mappings=[5="TEST SCENE"] visibility=[CNF_SCENE_EditMode==ON]
}
}
Now, let’s see the result:
First UI page:
Go inside:
Enable Edit Mode:
First, select scene that we want to edit:
Then back:
Now, to edit scene we have two option:
(1) Configure manually lights: menù Configure
then back again and press SAVE.
If you want to ignore a room during scene storing you can use the Ignore ROOM x switchs.
(2) “Learn” lights status from actual status: you have to switch/dim lights in your house as you want, then press button LEARN to save that configuration to be reused.
(If you want to reset a scene press *Cancel then SAVE
Finally, you can test scene by pressing TEST SCENE button: pre-saved lights status will be applyed to real ones
Enjoy!