Continuing the discussion from A beginners nightmare:
As discussed in the previous topic the idea is to analyse my rule and see if there is room for simplification.
Thank you in advance for you ideas and input, they already helped a lot.
The comments in the files are in German, I apologize in advance.
Here is what I have:
I have some LED-Strips in my sleeping room and I want to show some nice fades on them.
These fade should automatically start when there is movement or when I open my door.
This is why I have installed a movement sensor in my sleeping room and on its door.
Of course, I don’t always want the same fade to happen.
In the morning, I want a nice warm fade when I wake up and stand up. During the day I want a discreet fade, in the evening I want a bright fade so I don’t have to turn on the lights and in the night I want a low-light-fade so I don’t get blinded.
On Friday and Saturday the night-fade shall start later, because I go to bed later. The morning fade should also start later, because I like to sleep in.
When I walk in and out my sleeping room more frequent, I want the lights to be on longer, because I am obviously looking for something and don’t want the lights go off when I am in the room.
When I open the door, the time-out should be longer then when there is movement but when I close it, the time-out should be shorted, because I don’t want the light to be on when I am not in the room.
I tried to implement everything as described and it works fine but the lack of classes makes the code bloated and hard to read. As suggested in the other thread there may be a better and more elegant approach and this should be discussed in this topic.
This is are my fades. Unfortunately I have to create for every fade an item.
Color Schlafzimmer_Rechts_Oben_FADE_OFF {dmx="CHANNEL[1/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Rechts_Unten_FADE_OFF {dmx="CHANNEL[4/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Mitte_Oben_FADE_OFF {dmx="CHANNEL[7/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Mitte_Unten_FADE_OFF {dmx="CHANNEL[10/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Links_Oben_FADE_OFF {dmx="CHANNEL[13/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Links_Unten_FADE_OFF {dmx="CHANNEL[16/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Spiegel_FADE_OFF {dmx="CHANNEL[19/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Bett_FADE_OFF {dmx="CHANNEL[22/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Rechts_Oben_FADE_DAY {dmx="CHANNEL[1/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Rechts_Unten_FADE_DAY {dmx="CHANNEL[4/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Mitte_Oben_FADE_DAY {dmx="CHANNEL[7/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Mitte_Unten_FADE_DAY {dmx="CHANNEL[10/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Links_Oben_FADE_DAY {dmx="CHANNEL[13/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Links_Unten_FADE_DAY {dmx="CHANNEL[16/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Spiegel_FADE_DAY {dmx="CHANNEL[19/3], ON[FADE|2000:0,255,255:-1]"}
Color Schlafzimmer_Bett_FADE_DAY {dmx="CHANNEL[22/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Rechts_Oben_FADE_NIGHT {dmx="CHANNEL[1/3], ON[FADE|0:0,0,0:-1]"}
Color Schlafzimmer_Rechts_Unten_FADE_NIGHT {dmx="CHANNEL[4/3], ON[FADE|0:0,0,0:-1]"}
Color Schlafzimmer_Mitte_Oben_FADE_NIGHT {dmx="CHANNEL[7/3], ON[FADE|0:0,0,0:-1]"}
Color Schlafzimmer_Mitte_Unten_FADE_NIGHT {dmx="CHANNEL[10/3], ON[FADE|0:0,0,0:-1]"}
Color Schlafzimmer_Links_Oben_FADE_NIGHT {dmx="CHANNEL[13/3], ON[FADE|0:0,0,0:-1]"}
Color Schlafzimmer_Links_Unten_FADE_NIGHT {dmx="CHANNEL[16/3], ON[FADE|0:0,0,0:-1]"}
Color Schlafzimmer_Spiegel_FADE_NIGHT {dmx="CHANNEL[19/3], ON[FADE|0:0,1,1:-1]"}
Color Schlafzimmer_Bett_FADE_NIGHT {dmx="CHANNEL[22/3], ON[FADE|0:0,1,1:-1]"}
Color Schlafzimmer_Rechts_Oben_FADE_DAWN { dmx="CHANNEL[1/3], ON[FADE|1000:0,0,0:100|1000:255,255,255:-1]"}
Color Schlafzimmer_Rechts_Unten_FADE_DAWN { dmx="CHANNEL[4/3], ON[FADE|1000:255,255,255:100|2000:0,0,0:-1]"}
Color Schlafzimmer_Mitte_Oben_FADE_DAWN { dmx="CHANNEL[7/3], ON[FADE|1000:0,0,0:100|1000:255,255,255:-1]"}
Color Schlafzimmer_Mitte_Unten_FADE_DAWN {dmx="CHANNEL[10/3], ON[FADE|1000:255,255,255:100|2000:0,0,0:-1]"}
Color Schlafzimmer_Links_Oben_FADE_DAWN {dmx="CHANNEL[13/3], ON[FADE|1000:0,0,0:100|1000:255,255,255:-1]"}
Color Schlafzimmer_Links_Unten_FADE_DAWN {dmx="CHANNEL[16/3], ON[FADE|1000:255,255,255:100|2000:0,0,0:-1]"}
Color Schlafzimmer_Spiegel_FADE_DAWN {dmx="CHANNEL[19/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Bett_FADE_DAWN {dmx="CHANNEL[22/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Rechts_Oben_FADE_MORNING { dmx="CHANNEL[1/3], ON[FADE|10000:255,100,0:40000|10000:255,255,255:-1]"}
Color Schlafzimmer_Rechts_Unten_FADE_MORNING { dmx="CHANNEL[4/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Mitte_Oben_FADE_MORNING { dmx="CHANNEL[7/3], ON[FADE|10000:255,100,0:40000|10000:255,255,255:-1]"}
Color Schlafzimmer_Mitte_Unten_FADE_MORNING {dmx="CHANNEL[10/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Links_Oben_FADE_MORNING {dmx="CHANNEL[13/3], ON[FADE|10000:255,100,0:40000|10000:255,255,255:-1]"}
Color Schlafzimmer_Links_Unten_FADE_MORNING {dmx="CHANNEL[16/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Spiegel_FADE_MORNING {dmx="CHANNEL[19/3], ON[FADE|1000:0,0,0:-1]"}
Color Schlafzimmer_Bett_FADE_MORNING {dmx="CHANNEL[22/3], ON[FADE|10000:255,100,0:40000|10000:255,255,255:-1]"}
These are my Sensors:
Schlafzimmer_Fenster_Alarm //Turns 1 if there is movement in the sleeping room
Schlafzimmer_Fenster_Alarm_Anz //Amount of alarms in the last 15 min
Schlafzimmer_Fenster_Helligkeit //Brightness in the sleeping room
cSchlafzimmerBegewungsmelder //possibility to swith the movement-sensor off
Flur_Schlafzimmertuere_Tuer //Door-Contact
And here comes my rule:
// Imports
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import java.util.HashMap
import java.util.LinkedHashMap
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
//Config
val HashMap< String, LinkedHashMap<String, Object>> FadeConfig = newHashMap(
"Spiegel" -> (newLinkedHashMap(
"FADE_OFF" -> Schlafzimmer_Spiegel_FADE_OFF,
"FADE_DAY" -> Schlafzimmer_Spiegel_FADE_DAY,
"FADE_NIGHT" -> Schlafzimmer_Spiegel_FADE_NIGHT,
"FADE_DAWN" -> Schlafzimmer_Spiegel_FADE_DAWN,
"FADE_MORNING" -> Schlafzimmer_Spiegel_FADE_MORNING,
"ACTIVE" -> "" as String ))
as LinkedHashMap<String, Object>,
"Bett" -> (newLinkedHashMap(
"FADE_OFF" -> Schlafzimmer_Bett_FADE_OFF,
"FADE_DAY" -> Schlafzimmer_Bett_FADE_DAY,
"FADE_NIGHT" -> Schlafzimmer_Bett_FADE_NIGHT,
"FADE_DAWN" -> Schlafzimmer_Bett_FADE_DAWN,
"FADE_MORNING" -> Schlafzimmer_Bett_FADE_MORNING,
"ACTIVE" -> "" as String ))
as LinkedHashMap<String, Object>,
"Schlafzimmer_Rechts_Oben" -> (newLinkedHashMap(
"FADE_OFF" -> Schlafzimmer_Rechts_Oben_FADE_OFF,
"FADE_DAY" -> Schlafzimmer_Rechts_Oben_FADE_DAY,
"FADE_NIGHT" -> Schlafzimmer_Rechts_Oben_FADE_NIGHT,
"FADE_DAWN" -> Schlafzimmer_Rechts_Oben_FADE_DAWN,
"FADE_MORNING" -> Schlafzimmer_Rechts_Oben_FADE_MORNING,
"ACTIVE" -> "" as String ))
as LinkedHashMap<String, Object>,
"Schlafzimmer_Rechts_Unten" -> (newLinkedHashMap(
"FADE_OFF" -> Schlafzimmer_Rechts_Unten_FADE_OFF,
"FADE_DAY" -> Schlafzimmer_Rechts_Unten_FADE_DAY,
"FADE_NIGHT" -> Schlafzimmer_Rechts_Unten_FADE_NIGHT,
"FADE_DAWN" -> Schlafzimmer_Rechts_Unten_FADE_DAWN,
"FADE_MORNING" -> Schlafzimmer_Rechts_Unten_FADE_MORNING,
"ACTIVE" -> "" as String ))
as LinkedHashMap<String, Object>,
"Schlafzimmer_Mitte_Oben" -> (newLinkedHashMap(
"FADE_OFF" -> Schlafzimmer_Mitte_Oben_FADE_OFF,
"FADE_DAY" -> Schlafzimmer_Mitte_Oben_FADE_DAY,
"FADE_NIGHT" -> Schlafzimmer_Mitte_Oben_FADE_NIGHT,
"FADE_DAWN" -> Schlafzimmer_Mitte_Oben_FADE_DAWN,
"FADE_MORNING" -> Schlafzimmer_Mitte_Oben_FADE_MORNING,
"ACTIVE" -> "" as String ))
as LinkedHashMap<String, Object>,
"Schlafzimmer_Mitte_Unten" -> (newLinkedHashMap(
"FADE_OFF" -> Schlafzimmer_Mitte_Unten_FADE_OFF,
"FADE_DAY" -> Schlafzimmer_Mitte_Unten_FADE_DAY,
"FADE_NIGHT" -> Schlafzimmer_Mitte_Unten_FADE_NIGHT,
"FADE_DAWN" -> Schlafzimmer_Mitte_Unten_FADE_DAWN,
"FADE_MORNING" -> Schlafzimmer_Mitte_Unten_FADE_MORNING,
"ACTIVE" -> "" as String ))
as LinkedHashMap<String, Object>,
"Schlafzimmer_Links_Oben" -> (newLinkedHashMap(
"FADE_OFF" -> Schlafzimmer_Links_Oben_FADE_OFF,
"FADE_DAY" -> Schlafzimmer_Links_Oben_FADE_DAY,
"FADE_NIGHT" -> Schlafzimmer_Links_Oben_FADE_NIGHT,
"FADE_DAWN" -> Schlafzimmer_Links_Oben_FADE_DAWN,
"FADE_MORNING" -> Schlafzimmer_Links_Oben_FADE_MORNING,
"ACTIVE" -> "" as String ))
as LinkedHashMap<String, Object>,
"Schlafzimmer_Links_Unten" -> (newLinkedHashMap(
"FADE_OFF" -> Schlafzimmer_Links_Unten_FADE_OFF,
"FADE_DAY" -> Schlafzimmer_Links_Unten_FADE_DAY,
"FADE_NIGHT" -> Schlafzimmer_Links_Unten_FADE_NIGHT,
"FADE_DAWN" -> Schlafzimmer_Links_Unten_FADE_DAWN,
"FADE_MORNING" -> Schlafzimmer_Links_Unten_FADE_MORNING,
"ACTIVE" -> "" as String ))
as LinkedHashMap<String, Object>
)
var HashMap<String, Object> FadeData = newHashMap(
"TIMER" -> null as Timer,
"TIMEOUT_IST" -> 0 as Integer,
"TIMEOUT_NEU" -> 0 as Integer,
"SOURCE_IST" -> "woher kam die Anforderung" as String,
"SOURCE_NEU" -> "woher kommt die Anforderung" as String,
"FADE_IST" -> "aktueller Fade" as String,
"FADE_NEU" -> "neuer Fade" as String
) as HashMap
val org.eclipse.xtext.xbase.lib.Functions$Function2 ExecuteFade = [
HashMap< String, LinkedHashMap<String, Object>> fade_config,
HashMap< String, Object> fade_data|
var timer = fade_data.get( "TIMER") as Timer
val new_fade = fade_data.get( "FADE_NEU")
val int timeout_neu = fade_data.get( "TIMEOUT_NEU") as Integer
var int timeout_ist = fade_data.get( "TIMEOUT_IST") as Integer
val fade_items_keys = fade_config.keySet()
//Plausi
if( new_fade != "FADE_OFF" && new_fade != "FADE_DAY" && new_fade != "FADE_NIGHT" && new_fade != "FADE_DAWN" && new_fade != "FADE_MORNING") {
logDebug( "Schlafzimmer", String::format( "Wrong fade parameter: %s", new_fade))
return false
}
//Wenn der Dimm-Anforderer der Gleiche ist darf er auch den Timeout ändern
if( fade_data.get( "SOURCE_NEU") == fade_data.get( "SOURCE_IST") && timeout_ist != timeout_neu) {
timeout_ist = timeout_neu
logDebug( "Schlafzimmer", String::format( "Change timeout to %3ds", timeout_neu))
//Timeout wurde ja geändert --> speichern!
fade_data.put( "TIMEOUT_IST", timeout_ist)
}
//Wenn wir bewusst ausschalten macht natürlich ein Timer keinen sinn
if( new_fade != "FADE_OFF") {
//Timer zum Ausschalten schieben oder erstellen
if( timer != null) {
logDebug( "Schlafzimmer", String::format( "Reschedule timer: %3ds", timeout_ist))
timer.reschedule( now.plusSeconds(timeout_ist))
}
else {
logDebug( "Schlafzimmer", String::format( "Create FADE_OFF timer: %3ds", timeout_neu))
//Timer, der alles aus schaltet
timer = createTimer(now.plusSeconds(timeout_neu)) [|
fade_data.put( "FADE_IST", "FADE_OFF")
logDebug( "Schlafzimmer", String::format( "Execute Fade : %s", "FADE_OFF"))
fade_items_keys.forEach[ key |
//Alle Einträge der Hashmap durchnudeln
var LinkedHashMap<String, Object> items = fade_config.get(key)
var fade_item = items.get( "FADE_OFF") as org.openhab.core.items.GenericItem
//Nur wenn der Schalter auf An ist senden wir den fade!
if( cSchlafzimmerBegewungsmelder.state == ON) sendCommand(fade_item, ON)
]
fade_data.put( "TIMER", null)
]
//Speichern!
fade_data.put( "TIMER", timer)
fade_data.put( "TIMEOUT_IST", timeout_neu)
fade_data.put( "SOURCE_IST", fade_data.get( "SOURCE_NEU"))
}
}
else {
//Falls noch einer läuft -> abbrechen!
if( timer != null) {
timer.cancel()
fade_data.put( "TIMER", null)
}
}
//Wenn kein Fade gemacht wird, setzen wir nur den Timer zurück
if( fade_data.get( "FADE_IST") == new_fade) return false
fade_data.put( "FADE_IST", new_fade)
//Fade-Code
logDebug( "Schlafzimmer", String::format( "Execute Fade : %s", new_fade))
fade_items_keys.forEach[ key |
//Alle Einträge der Hashmap durchnudeln
var LinkedHashMap<String, Object> items = fade_config.get(key)
var fade_item = items.get( new_fade) as org.openhab.core.items.GenericItem
sendCommand(fade_item, ON)
]
return false
]
val org.eclipse.xtext.xbase.lib.Functions$Function3 IdentifyFade = [
HashMap< String, LinkedHashMap<String, Object>> fade_config,
HashMap< String, Object> fade_data,
String caller|
var Number Helligkeit_Zimmer = Schlafzimmer_Fenster_Helligkeit.state as DecimalType
var int timeout = 60
//Anzahl Bewegungen
val int movement = (Schlafzimmer_Fenster_Alarm_Anz.state as DecimalType).intValue
logDebug( "Schlafzimmer", String::format( "Fade Request from %s! Brightness: %s Movement: %d", caller, Helligkeit_Zimmer, movement))
//------------------------------------------------------------------------
//Timeoutbestimmtung
if( caller == "Alarm") {
//Timeout bei Bewegungsmelder dynamisch
if( movement <= 2) timeout = 1 * 60
else if( movement <= 6) timeout = 5 * 60
else timeout = 10 * 60
}
else {
logDebug( "Schlafzimmer", String::format( "Türe: %s", Flur_Schlafzimmertuere_Tuer.state))
//Wenn wir die Türe öffnen ist der Timeout 10 min
if( Flur_Schlafzimmertuere_Tuer.state == OPEN) timeout = 10 * 60
else timeout = 1 * 45
}
val int Time_Hour = now.getHourOfDay()
val int dow = now.getDayOfWeek()
var int start_morning = 5
var int start_day = 8
var int start_night = 22
//Freitag + Samstag -> Abends länger hell
if( dow == 4 || dow == 5) {
start_night = 24
}
//Samstag + Sonntag -> Morgens später hell
if( dow == 5 || dow == 6) {
start_morning = 8
start_day = 10
}
//Auswählen, was wir machen
var String new_mode_tmp = "FADE_OFF"
if( Time_Hour < start_morning || Time_Hour >= start_night) {
new_mode_tmp = "FADE_NIGHT"
}
else if ( Time_Hour < start_day) {
new_mode_tmp = "FADE_MORNING"
}
else if ( Time_Hour < start_night) {
if( Helligkeit_Zimmer < 15) new_mode_tmp = "FADE_DAWN"
else new_mode_tmp = "FADE_DAY"
}
//Fade-Data speichern
fade_data.put( "SOURCE_NEU", caller)
fade_data.put( "FADE_NEU", new_mode_tmp)
fade_data.put( "TIMEOUT_NEU", timeout)
return false
]
//Damit wir nicht multithread auf die hashmaps zugreifen!
var Lock mylock = new ReentrantLock()
rule "Schlafzimmer Alarm"
when
Item Schlafzimmer_Fenster_Alarm changed from 0 to 1
then
//Bewegungsmelder ist aus
if( cSchlafzimmerBegewungsmelder.state != ON) return false
mylock.lock()
IdentifyFade.apply( FadeConfig, FadeData, "Alarm")
ExecuteFade.apply( FadeConfig, FadeData)
mylock.unlock()
end
rule "Schlafzimmer Türe changed"
when
Item Flur_Schlafzimmertuere_Tuer changed
then
//Bewegungsmelder ist aus
if( cSchlafzimmerBegewungsmelder.state != ON) return false
mylock.lock()
IdentifyFade.apply( FadeConfig, FadeData, "Türe")
ExecuteFade.apply( FadeConfig, FadeData)
mylock.unlock()
end