Timers in a rule

Hi,

I have a rule with some timers in it. The rule seems to work fine but I get the following message in the log every time I change and save the rule. What does it mean and do I have to care about it or isn‘t it of importance?

2019-08-15 17:13:03.832 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'verschattung_rollos_ost_ab.rules', using it anyway:
The value of the local variable timer_6 is not used
The value of the local variable timer_7 is not used
The value of the local variable timer_8 is not used
The value of the local variable timer_9 is not used
The value of the local variable timer_10 is not used
The value of the local variable timer_11 is not used

We cannot see the rule from here. Please post it, using code fences.

Here‘s the rule:

var boolean log = true
//val azimut_soll_ost_ab = 77
//val temperatur_soll_ost_ab = 10
//val bewoelkung_soll_ost_ab = 75
//val elevation_soll_ost_ab = 12
//val rollos_zielwert_ost_ab = 90


rule "Rollos Ostseite abfahren"
when Item astro_azimuth changed
then
    val String logPrefix_ost = 'Rolloautomatik Ost Rollos ab - '
    if (log) logInfo('rules', logPrefix_ost + 'Regel wurde gestartet')

    var String timeLast_ost = 'xxxx-xx-xx'
    if (rolloautomatik_ost_start_last.state == NULL) {
        if (log) logInfo('rules', logPrefix_ost + 'Erstmalige Ausführung am System, Belegung mit Initialwert')
        
    } else {
        timeLast_ost = rolloautomatik_ost_start_last.state.toString().substring(0,10)
    }
	var String timeNow = now.toString().substring(0,10)
	
	var Timer_6 timer_6 = null           // für Rollo 2 WZ
	var Timer_7 timer_7 = null           // für Rollo 3 WZ
	var Timer_8 timer_8 = null           // für Rollo 5 EZ
	var Timer_9 timer_9 = null           // für Rollo SZ Rollo 14
	var Timer_10 timer_10 = null         // für Rollo GZ Rollo 17
	var Timer_11 timer_11 = null         // für Rollo 19 KiZi1 (Gaube)


	if ((WZ_Fenster_02.state=="CLOSED") && (WZ_Fenster_03.state=="CLOSED") && (WZ_Fenster_04.state=="CLOSED") && (EZ_Fenster_05.state=="CLOSED") && (AZ_Fenster_12.state=="CLOSED") && (AZ_Fenster_13.state=="CLOSED")) {
		if (rolloautomatik_ost.state == ON) {
			if (timeNow != timeLast_ost) {
				if ((astro_azimuth.state > Integer::parseInt(rolloautomatik_azimuth_ost_start.state.toString())) && (astro_azimuth.state < Integer::parseInt(rolloautomatik_azimuth_ost_ende.state.toString()))) {
					if (wetter_temperatur_2.state > Integer::parseInt(rolloautomatik_temp_min.state.toString())) {
						if (wetter_bewoelkung_2.averageSince(now.minusMinutes(30)) <= Integer::parseInt(rolloautomatik_wolken_max.state.toString())) { 
							if (astro_elevation.state > Integer::parseInt(rolloautomatik_elevation_ost_start.state.toString())) {
                        
								// Rollos runterfahren
								if (log) logInfo('rules', logPrefix_ost + 'Rollos Ost werden abgefahren')
                            
									if (WZ_Rollo_02_Pos.state <= Integer::parseInt(rolloautomatik_zielwert.state.toString())) {
									if (log) logInfo('rules', logPrefix_ost + 'Fahre Rolladen Ost auf ' + rolloautomatik_zielwert.state.toString() + '%: ' + 'WZ Rollo 2')
										logInfo("WZ Rollo 2", "Actual Rollo Position: " + WZ_Rollo_02_Pos.state)
										WZ_Rollo_02.sendCommand(DOWN)
										timer_6 = createTimer(now.plusSeconds(17), [|
										logInfo("Rollo 2 WZ", "Timer expired and Rollo WZ 2 set to STOP")
										WZ_Rollo_02.sendCommand(STOP)	
										])
										logInfo("WZ Rollo 2 auf 90%", "WZ Rollo 2 auf 90%")
										WZ_Rollo_02_Pos.sendCommand(90) // wir geben die aktuelle Position ans Item
									} 
								
									if (WZ_Rollo_03_Pos.state <= Integer::parseInt(rolloautomatik_zielwert.state.toString())) {
									if (log) logInfo('rules', logPrefix_ost + 'Fahre Rolladen auf ' + rolloautomatik_zielwert.state.toString() + '%: ' + 'WZ Rollo 3')
										logInfo("WZ Rollo 3", "Actual Rollo Position: " + WZ_Rollo_03_Pos.state)
										WZ_Rollo_03.sendCommand(DOWN)
										timer_7 = createTimer(now.plusSeconds(17), [|
										logInfo("WZ Rollo 3", "Timer expired and WZ Rollo 3 set to STOP")
										WZ_Rollo_03.sendCommand(STOP)	
										])
										logInfo("WZ Rollo 3 auf 90%", "WZ Rollo 3 auf 90%")
										WZ_Rollo_03_Pos.sendCommand(90) // wir geben die aktuelle Position ans Item
									}
                                
									if (EZ_Rollo_05_Pos.state <= Integer::parseInt(rolloautomatik_zielwert.state.toString())) {
									if (log) logInfo('rules', logPrefix_ost + 'Fahre Rolladen auf ' + rolloautomatik_zielwert.state.toString() + '%: ' + 'EZ Rollo 5')
										logInfo("EZ Rollo 5", "Actual Rollo Position: " + EZ_Rollo_05_Pos.state)
										EZ_Rollo_05.sendCommand(DOWN)
										timer_8 = createTimer(now.plusSeconds(17), [|
										logInfo("EZ Rollo 5", "Timer expired and EZ Rollo 5 set to STOP")
										EZ_Rollo_05.sendCommand(STOP)	
										])
										logInfo("EZ Rollo 5 auf 90%", "EZ Rollo 5 auf 90%")
										EZ_Rollo_05_Pos.sendCommand(90) // wir geben die aktuelle Position ans Item
									}
                                
									if (SZ_Rollo_14_Pos.state <= Integer::parseInt(rolloautomatik_zielwert.state.toString())) {
									if (log) logInfo('rules', logPrefix_ost + 'Fahre Rolladen auf ' + rolloautomatik_zielwert.state.toString() + '%: ' + 'SZ Rollo 14')
										logInfo("SZ Rollo 14", "Actual Rollo Position: " + SZ_Rollo_14_Pos.state)
										SZ_Rollo_14.sendCommand(DOWN)
										timer_9 = createTimer(now.plusSeconds(17), [|
										logInfo("SZ Rollo 14", "Timer expired and SZ Rollo 14 set to STOP")
										SZ_Rollo_14.sendCommand(STOP)	
										])
										logInfo("SZ Rollo 14 auf 90%", "SZ Rollo 14 auf 90%")
										SZ_Rollo_14_Pos.sendCommand(90) // wir geben die aktuelle Position ans Item
									} 
                                
									if (GZ_Rollo_17_Pos.state <= Integer::parseInt(rolloautomatik_zielwert.state.toString())) {
									if (log) logInfo('rules', logPrefix_ost + 'Fahre Rolladen auf ' + rolloautomatik_zielwert.state.toString() + '%: ' + 'GZ Rollo 17')
										logInfo("GZ Rollo 17", "Actual Rollo Position: " + GZ_Rollo_17_Pos.state)
										GZ_Rollo_17.sendCommand(DOWN)
										timer_10 = createTimer(now.plusSeconds(18), [|
										logInfo("GZ Rollo 17", "Timer expired and GZ Rollo 17 set to STOP")
										GZ_Rollo_17.sendCommand(STOP)	
										])
										logInfo("GZ Rollo 17 auf 90%", "GZ Rollo 17 auf 90%")
										GZ_Rollo_17_Pos.sendCommand(90) // wir geben die aktuelle Position ans Item
									}    
                                
									if (KiZi1_Rollo_19_Pos.state <= Integer::parseInt(rolloautomatik_zielwert.state.toString())) {
									if (log) logInfo('rules', logPrefix_ost + 'Fahre Rolladen auf ' + rolloautomatik_zielwert.state.toString() + '%: ' + 'KiZi1 Rollo 19 Gaube')
										logInfo("KiZi1 Rollo 19", "Actual Rollo Position: " + KiZi_Rollo_19_Pos.state)
										KiZi1_Rollo_19.sendCommand(DOWN)
										timer_11 = createTimer(now.plusSeconds(16), [|
										logInfo("KiZi1 Rollo 19 Gaube", "Timer expired and KiZi1 Rollo 19 set to STOP")
										KiZi1_Rollo_19.sendCommand(STOP)	
										])
										logInfo("KiZi1 Rollo 19 Gaube auf 90%", "KiZi1 Rollo 19 Gaube auf 90%")
										KiZi1_Rollo_19_Pos.sendCommand(90) // wir geben die aktuelle Position ans Item
										}                       
                                		
									else {
										if (log) logInfo('rules', logPrefix_ost + 'Rolladen (' + WZ_Rollo_02_Pos.state.toString() + '%) oder (' + WZ_Rollo_03_Pos.state.toString() + '%) oder (' + EZ_Rollo_05_Pos.state.toString() + '%) oder (' + SZ_Rollo_14_Pos.state.toString() + '%) oder (' + GZ_Rollo_17_Pos.state.toString() + '%) oder (' + KiZi1_Rollo_19_Pos.state.toString() + '%) ist bereits weiter geschlossen als er geschlossen werden sollte und wird daher ignoriert')
									}
							
									// Letzte Ausführung mit entsprechendem Zeitstempel belegen
									sendBroadcastNotification("Verschattung Ostseite aktiv") //Pushnachricht
									rolloautomatik_ost_start_last.postUpdate(now.toString())
                            
							} else {
								if (log) logInfo('rules', logPrefix_ost + 'Sollwert Elevation für Abfahren (' + rolloautomatik_elevation_ost_start.state.toString() + ') wurde noch nicht erreicht, aktuelle Elevation ist (' + astro_elevation.state.toString() + ')')
							}
						} else {
							if (log) logInfo('rules', logPrefix_ost + 'Mindestbewoelkung (' + rolloautomatik_wolken_max.state.toString() + ') wurde nicht unterschritten, aktuelle mittl. Bewoelkung ist (' + (wetter_bewoelkung_2.averageSince(now.minusMinutes(30))) + ')')
						}
					} else {
						if (log) logInfo('rules', logPrefix_ost + 'Mindest-Temperatur (' + rolloautomatik_temp_min.state.toString() + ') wurde nicht erreicht durch aktuelle Temperatur (' + wetter_temperatur_2.state.toString() + ')')
					}
				} else {
					if (log) logInfo('rules', logPrefix_ost + ' Sollwert Azimuth für Abfahren (' + rolloautomatik_azimuth_ost_start.state.toString() + ') wurde noch nicht erreicht, aktueller Azimuth ist (' + astro_azimuth.state.toString() + ')')
				}
			} else {
				if (log) logInfo('rules', logPrefix_ost + 'Automatik heute bereits einmal gelaufen (' + rolloautomatik_ost_start_last.state.toString().substring(11,19) + '), wird daher ignoriert')
			}
		} else {
			if (log) logInfo('rules', logPrefix_ost + 'Beende, da Automatik generell nicht aktiv')
		}	
	} else {
         if (log) logInfo('rules', logPrefix_ost + 'Beende, da mindestens ein großes Fenster im WZ, EZ oder AZ offen')
    }
end

@rlkoshak has simplified many rules for people.

Wow that’s a lot of nested if statements.

First, the validation issues.

Each of those variables listed in the message are defined but never used. It’s pointless to define a variable and never use that variable, hence the warning.

But this is only a symptom of a whole bunch of much more fundamental problems.

  • There is no Timer_6 type. There is only a Timer type so you should have.
	var Timer timer_6 = null           // für Rollo 2 WZ
	var Timer timer_7 = null           // für Rollo 3 WZ
	var Timer timer_8 = null           // für Rollo 5 EZ
	var Timer timer_9 = null           // für Rollo SZ Rollo 14
	var Timer timer_10 = null         // für Rollo GZ Rollo 17
	var Timer timer_11 = null         // für Rollo 19 KiZi1 (Gaube)
  • You never look at these timers again, unless you have some other Rule you are not showing, so there is no reason for timer_6 through timer_11 to even exist. Replace

    timer_6 = createTimer(now.plusSeconds(17), [|

with

createTimer(now.plusSeconds(17), [|
  • Why are you taking something that is a Number, converting it to a String and then parsing that String back into a Number (Integer is a Number) for comparisons? Just use

    if(astro_azimuth.state > rolloautomatic_azimuth_ost_start.state)

or if that doesn’t work

if(astro_azimuth.state as Number > rolloautomatic_azimuth_ost_start.state as Number)
  • Be consistent with your indents. There are some of those if statements that are even further indented. Indentation is vital to help humans understand the code (the computer doesn’t care).

  • If the Fenster Items are Contact Items, you need to compare them to CLOSED, not the String “CLOSED”.

OK, now for simplifications. Honesty, this code is too complex for me to quickly scan and understand what it does (and I don’t read German). But I do see a number of things that might help simplify the Rules.

if ((WZ_Fenster_02.state!="CLOSED") && (WZ_Fenster_03.state!="CLOSED") && (WZ_Fenster_04.state!="CLOSED") && (EZ_Fenster_05.state!="CLOSED") && (AZ_Fenster_12.state!="CLOSED") && (AZ_Fenster_13.state!="CLOSED")) {
    if (log) logInfo('rules', logPrefix_ost + 'Beende, da mindestens ein großes Fenster im WZ, EZ oder AZ offen')
    return;
}

if (rolloautomatik_ost.state == ON) {
    if (log) logInfo('rules', logPrefix_ost + 'Beende, da Automatik generell nicht aktiv')
    return;
}

// and so on

If you do that you will end up with just one or two indents and have much easier to understand code.

  • Consider moving some of these calculations outside of this Rule. For example, if you put all those WZ_Fenster Items into a Group:Contact:OR(OPEN, CLOSED) Fensters you can replace that first really long if statement with

    if(Fensters.state != CLOSED

Similarly, you can move some comparisons into their own Rule that saves the result in an Item. For example, have an astro_azimuth triggered Rule that all it does is determine whether the rollershutters need to be adjusted and if so send a command to an Item. Then you have another Rule triggered by this other Item to do the work of adjusting the Items.

  • There is a lot of duplicated code here. If you apply How to Structure a Rule (linked to in the DRY post above) you should be able to just determine what values you need to adjust the rollershutters or not and then use the same code to adjust them all.

  • The whole purpose of the first argument to logInfo is to get your logPrefix_ost value, why prepend it to the log statement?

  • You usually only have to specify the type of a variable only if you will initialize it to null.

  • The usual way to handle logging/not logging is to use logging levels. So instead of an if statement in front of every log statement, use logDebug and if you ever need to turn on the logging open the Karaf Console and change the logging level for org.eclipse.esh.Script.Rolloautomatik Ost Rollos ab - (or something like that, I don’t have access to my home network right now to check, to debug.

I’m just typing the following in to the browser, it likely has a bunch of typos.

rule "Rollos Ostseite abfahren"
when Item astro_azimuth changed
then
    val logPrefix_ost = 'Rolloautomatik Ost Rollos ab - '
    logDebug(logPrefix_ost, 'Regel wurde gestartet')

    val timeLast_ost = if(rolloautomatik_ost_start_last.state == NULL) now.minusDays(1) else new DateTime(rolloautomatik_ost_start_last.state.toString())

    if(Fensters.state != CLOSED){
        logDebug(logPrefix_ost, 'Beende, da mindestens ein großes Fenster im WZ, EZ oder AZ offen')
        return;
    }

    if(rolloautomatik_ost.state != ON) {
        logDebug(logPrefix_ost, 'Beende, da Automatik generell nicht aktiv')
        return;
    }

    if(astro_azimuth.state <= rolloautomatik_azimuth_ost_start.state && astro_azimuth.state >= rolloautomatik_azimuth_ost_ende.state) {
        logDebug(logPrefix_ost, 'Automatik heute bereits einmal gelaufen (' + rolloautomatik_ost_start_last.state.toString().substring(11,19) + '), wird daher ignoriert')
        return;
    }

    if(wetter_temperatur_2.state < rolloautomatik_temp_min.state) {
        logDebug(logPrefix_ost, ' Sollwert Azimuth für Abfahren (' + rolloautomatik_azimuth_ost_start.state.toString() + ') wurde noch nicht erreicht, aktueller Azimuth ist (' + astro_azimuth.state.toString() + ')')
        return;
    }

    if (wetter_bewoelkung_2.averageSince(now.minusMinutes(30)) > rolloautomatik_wolken_max.state){
        logDebug(logPrefix_ost, 'Mindest-Temperatur (' + rolloautomatik_temp_min.state.toString() + ') wurde nicht erreicht durch aktuelle Temperatur (' + wetter_temperatur_2.state.toString() + ')')
        return;
    }

    if (astro_elevation.state <= rolloautomatik_elevation_ost_start.state.toString()) {
        logDebug(logPrefix_ost, 'Sollwert Elevation für Abfahren (' + rolloautomatik_elevation_ost_start.state.toString() + ') wurde noch nicht erreicht, aktuelle Elevation ist (' + astro_elevation.state.toString() + ')')
        return;
    }

    // NOTE: Create a Group to hold all the Rollo_Pos
    val Map<String, Integer> downSeconds = newHashMap("WZ" -> 17, "SZ" -> 17, "GZ" -> 18, "KiZi1" -> 16)
    Rollo_Pos.members.filter[ pos | pos.state <= rolloautomatik_zielwert.state ].forEach[ pos |    // Working with Groups in Rules Design Pattern
        logDebug(logPrefix_ost, 'Fahre Rolladen Ost auf ' + rolloautomatik_zielwert.state.toString() + '%: ' + pos.name)
        val rolloName = pos.name.replace("_Pos", "") 
        sendCommand(rolloName, "DOWN") // Associated Items Design Pattern
        createTimer(now.plusSeconds(downSeconds.get(pos.name.split("_").get(0), [ |
            logInfo(rolloName, "Timer expired for " + rolloName + " set to STOP")
            sendCommand(rolloName, "STOP")
        ]
        logInfo(rolloName, rolloName + " auf 90%")
        pos.sendCommand(90)
    ]

    // Letzte Ausführung mit entsprechendem Zeitstempel belegen
    sendBroadcastNotification("Verschattung Ostseite aktiv") //Pushnachricht
    rolloautomatik_ost_start_last.postUpdate(now.toString())
end
2 Likes

Wow Rick, Thank you so much for your swift reply and your comprehensive description of possible simplifications and improvements in the rule. I really appreciate that and will try out my best to modify it based on your valuable hints!!!

Thanks again and best regards.

1 Like