"Trinary Switch" - if condition in case rule

Hi there,

I’m trying to set up some simple alarm system rules. Unfortunatley I’m struggeling with - I think - the case part …

// ITEMS Alarmanlage
    Group GreedALARM            // Group for trigger purposes
    Number alarmmodus           // Alarm system mode: off/on/auto
    Switch alarmstill           // Silent Alarm 
    Switch alarmswitch          // Switch to switch system off manually before siren starts
rule "Alarmanlage"
when
    Member of GreedALARM changed to OPEN 
then
    switch (alarmmodus.state as DecimalType) {
        case 0 :    { 
                    logInfo("RULE", "--> Alarmanlage [AUS]: Reed ausgelöst, aber Modus:AUS - "+ triggeringItem.name)
                    }
        
        case 1 :    { 
                    logInfo("RULE", "--> Alarmanlage [AN]: Reed ausgelöst - "+ triggeringItem.name)
                    alarmswitch.sendCommand(ON)
                            sendCommand(out_xiao_gateway_soundvolume, 2)
                            sendCommand(out_xiao_gateway_sound, 11)
                            Thread::sleep(2000) /* wait for 2 seconds */
                            sendCommand(out_xiao_gateway_sound, 10000)
                            sendCommand(out_xiao_gateway_soundvolume, 0)
                            Thread::sleep(3000) /* wait for 3 seconds */
                            if(alarmswitch.state==ON) {
                                if(alarmstill.state=ON) {
                                    logInfo("RULE", "--> Alarmanlage [AN]: stiller Alarm")
                                    // sendPushoverMessage(pushoverBuilder("ALARMANLAGE [AN]: Reed-Kontakt ausgelöst - stiller Alarm").withPriority(1).withSound("spacealarm"))
                                    postUpdate(EventLog, "Alarmanlage [AN]: stiller Alarm")        // eventlog.rules
                                    Thread::sleep(3000) /* wait for 3 seconds */
                                    alarmswitch.sendCommand(OFF)
                                }
                                if(alarmstill.state=OFF) {
                                    logInfo("RULE", "--> Alarmanlage [AN]: stiller Alarm")
                                    // sendPushoverMessage(pushoverBuilder("ALARMANLAGE: Reed-Kontakt ausgelöst - lauter ALARM !!!").withPriority(1).withSound("spacealarm"))
                                    postUpdate(EventLog, "Alarmanlage [AN]: lauter Alarm")        // eventlog.rules
                                    // 
                                    Thread::sleep(3000) /* wait for 3 seconds */
                                    alarmswitch.sendCommand(OFF)
                                }
                            }
                    }

        case 2 :    {
                    logInfo("RULE", "--> Alarmanlage [AUTO]: Reed ausgelöst - "+ triggeringItem.name) 
                    // code for auto-mode tbd
                    }
    }
end
sitemap test label="Test" {

    Frame label="Alarmanlage" {
        Group item=GreedALARM
        Switch item=alarmmodus mappings=[2="Auto", 1="An", 0="Aus"]
        Switch item=alarmstill
        Switch item=alarmswitch
    }    
}
  • For case 0 it works as expected.
  • For case1 it seems to stop after Thread::sleep(3000) /* wait for 3 seconds */

My question is: Is it possible to have a if-condition within the “case”-condition as it is in the code above?

Thanks
Selter

Yes, that’s no problem.

I note that if your condition alarmswitch.state==ON is not satisfied, nothing else will happen. Not sure how you tell it’s “stopped” from it doing nothing before exiting.

Warning: a few lines earlier, you send the command ON to alarmswitch. That won’t change the state of alarmswitch immediately - although here you’re not testing it until some seconds later, so it should have caught up by then.

logInfo() is useful tool to follow what’s going on. Try some adding more

   case 1 :    { 
                    logInfo("RULE", "--> Alarmanlage [AN]: Reed ausgelöst - "+ triggeringItem.name)
                    alarmswitch.sendCommand(ON)
 ...
                            logInfo("test". "switch state is " + alarmswitch.state.toString)
                            if(alarmswitch.state==ON) {
                                logInfo("test". "still state is " + alarmstill.state.toString)
                                if(alarmstill.state=ON) {
...  etc 
1 Like

You have an error in this lines:

if(alarmstill.state=ON)
                   ^ missing =, should be ==
if(alarmstill.state=OFF)
                   ^ missing =, should be ==

:slight_smile:

2 Likes

Another thing… It’s a bad idea to use Thread::sleep() for long sleep statements, as it would block a rule thread. I would consider to do it with a timer as a very simple state machine:

var Timer tAblauf = null                                                                            // timer of state machine
var Number nSchritt                                                                                 // counter of state machine

rule "Alarmanlage"
when
    Member of GreedALARM changed to OPEN 
then
    if(!(alarmmodus.state instanceof Number)) {                                                     // check null state
        logInfo("Alarm","Alarmmodus not set!")
        return;
    }
    val Number nAl = alarmmodus.state as Number
    val String sModus = switch(nAl){case 0:"AUS" case 1:"AN" case 2:"AUTO"}
    val String sMyLog = "Alarmanlage [" + sModus + "]: Reed ausgelöst" + if(nAl==0) ", aber Modus:AUS - " else " - "
    logInfo("Alarm", sMyLog + triggeringItem.name)
    switch  (nAl) {
        case 0 : {
                                                                                                    // empty :)
        }
        case 1 : {
            nSchritt = 1                                                                            // set counter of state machine
            if(tAblauf !== null) tAblauf.cancel
            tAblauf = createTimer(now, [ |                                                          // start state machine
                switch (nSchritt) {
                    case 1 : {                                                                      // 1st step
                        alarmswitch.sendCommand(ON)
                        out_xiao_gateway_soundvolume.sendCommand(2)
                        out_xiao_gateway_sound.sendCommand(11)
                        tAblauf.reschedule(now.plusSeconds(2))                                      // set timer for next step
                    }
                    case 2 : {                                                                      // 2nd step
                        out_xiao_gateway_sound.sendCommand(10000)
                        out_xiao_gateway_soundvolume.sendCommand(0)
                        tAblauf.reschedule(now.plusSeconds(3))                                      // set timer for next step
                    }
                    case 3: {                                                                       // 3rd step
                        if(alarmswitch.state != OFF) {                                              // alarm stopped?
                            val String sAlarm = if(alarmstill.state == ON) "stiller" else "lauter"
                            logInfo("Alarm", "--> Alarmanlage [AN]: " + sAlarm + " Alarm")
                            // sendPushoverMessage(pushoverBuilder("ALARMANLAGE [AN]: Reed-Kontakt ausgelöst - " + sAlarm + " Alarm").withPriority(1).withSound("spacealarm"))
                            EventLog.postUpdate("Alarmanlage [AN]: " + sAlarm + " Alarm")
						}	
                        tAblauf.reschedule(now.plusSeconds(3))                                      // set timer for next step
                    }
                    case 4: {                                                                       // last step
                        alarmswitch.sendCommand(OFF)
                        tAblauf = null
                    }
                }
                nSchritt += 1                                                                       // count up
            ])
        }
        case 2 : {
                                                                                                    // code for auto-mode tbd
        }
    }
end

rule "Alarmanlage 2"
when
    Item alarmswitch changed to OFF                                                                 // alarm stopped
then
    if(tAblauf !== null && nSchritt > 2) {                                                          // only stop state machine after the 2nd step!
        tAblauf.cancel
        tAblauf = null
    }
end

2 Likes

Many thanks fpr the hint :wink:

Interesting point.
I’ll look into this - your new code is quite different from mine and not so easy to understand for a beginner …

Well, I wrote comments inline (you will have to scroll to the right - or just copy the whole code to an editor to read it)
The code should do exactly the same thing as yours (almost - one of the logInfos might be slightly different…)

Concerning my code in posting #1 - the simplest (but a bit dirty) way is to replace the thread:sleep with createTimer - right?

For case 1 it would be like this:

case 1 :    { 
                    logInfo("RULE", "--> Alarmanlage [AN]: Reed ausgelöst - "+ triggeringItem.name)
                    alarmswitch.sendCommand(ON)
                        sendCommand(out_xiao_gateway_soundvolume, 10)
                        sendCommand(out_xiao_gateway_sound, 8)
                        // Thread::sleep(3000) 																// wait for 3 seconds 
						createTimer(now.plusSeconds(3), [|
							sendCommand(out_xiao_gateway_sound, 10000)
							sendCommand(out_xiao_gateway_soundvolume, 0)
							])
                        // Thread::sleep(10000) 															// wait for 10 seconds ... zum deaktivieren des alarmswitch 
						createTimer(now.plusSeconds(10), [|
                            if(alarmswitch.state==ON) {
                                if(alarmstill.state==ON) {
                                    logInfo("RULE", "--> Alarmanlage [AN]: stiller Alarm")
                                     sendPushoverMessage(pushoverBuilder("ALARMANLAGE [AN]: Reed-Kontakt ausgelöst - stiller Alarm").withPriority(1).withSound("spacealarm"))
                                    postUpdate(EventLog, "Alarmanlage [AN]: stiller Alarm")        			// eventlog.rules
                                    // Thread::sleep(3000) // wait for 3 seconds 
									createTimer(now.plusSeconds(10), [|
										alarmswitch.sendCommand(OFF)
									])
                                }
                                if(alarmstill.state==OFF) {
                                    logInfo("RULE", "--> Alarmanlage [AN]: lauter Alarm")
                                     sendPushoverMessage(pushoverBuilder("ALARMANLAGE: Reed-Kontakt ausgelöst - lauter ALARM !!!").withPriority(1).withSound("spacealarm"))
                                     // Licht an + Rollladen hoch
                                    postUpdate(EventLog, "Alarmanlage [AN]: lauter Alarm")        			// eventlog.rules
                                        sendCommand(out_xiao_gateway_soundvolume, 10)
                                        sendCommand(out_xiao_gateway_sound, 7)
                                        // Thread::sleep(20000) 											// wait for 20 seconds 
										createTimer(now.plusSeconds(20), [|
											sendCommand(out_xiao_gateway_sound, 10000)
											sendCommand(out_xiao_gateway_soundvolume, 0)
										])
										// Thread::sleep(3000) 												// wait for 3 seconds 
									createTimer(now.plusSeconds(10), [|
										alarmswitch.sendCommand(OFF)
									])
                                }
                            }
						])
                    }

I’m not sure what your intentions were, but do be aware that createTimer() does what it says on the tin - creates the timer (to be executed later) and immediately moves on.

In this case, there’s some sound action, then the alarmswitch OFF will fire after 10 seconds, and then more sound will fire after another 10 seconds.

The timers do their thing on time, not creation order.

Another thing with Timers…

Please be aware that you can simply create a timer without assigning it to a var, but you will never be able to get any information or control to this timer.

Your task is a really simple state machine (as said in my first posting) - imagine an old washing machine, which has a programming wheel in it, a dumb “do this - wait x seconds - do that - wait y seconds - start motor - wait z seconds - do another thing, wait…” - This is how the rule works, and it takes care of breaking points to stop the job.
Be aware that my rule does only use one timer, where you want to use 5 timers, which is bad design. Try to be efficient with the code. :slight_smile: