[SOLVED] Missing EOF at 'var' between rules. Error?!

Hello,

i have 2 rules in on file.

Please tell me why do i get this error and how can i solve it?

PS
If i put this

var Timer timer = null 
var Number repeatTimer

at the top of the rule (before first rule) i get another error

Please advice me what have i done wrong?

Thanks a lot!

You certainly have to put any declarations at the top, before rules.

Your second error is about the var repeatTimer, which you have now declared inside the rule.
When the rule finishes, that local variable is destroyed.
When the timer runs later, and wants to use repeatTimer

A cure is to declare the var globally, so that it persists between rule runs. At the top, outside of rules.

In general it is more helpful to copy/paste code in code fences here, than use screenshots.

1 Like

Thanks i will try!
And one more error

I use it, so why do i get it?

Because you fully specify the path of ReentrantLock, you are not using the import.

Sorry i use screenshot here because so i can aim on the error.
But is the code needed, here it is.

import java.util.concurrent.locks.ReentrantLock

val Number MODE_OFF = 0
val Number MODE_STANDBY = 1
val Number MODE_ACTIVE = 2
val Number MODE_FINISHED = 3

//val Number DEBOUNCE_TIME = 90000 
val Number TELEPERIOD_ACTIVE = 30
val Number TELEPERIOD_OFF = 300

var Timer timer = null 
var Number repeatTimer

val String messagePrefix = "householdDevices.rules | Washingmachine  | "
var java.util.concurrent.locks.ReentrantLock finishLock  = new java.util.concurrent.locks.ReentrantLock()

//------------------------------------------------------------------------//
//   WASCHINGMACHINE
//------------------------------------------------------------------------//
rule "Washingmachine Consumption"
when
    Item TasmotaSocketDryerEnergyPower received update
then
    if (WashingmachineState.state == MODE_ACTIVE) {
        WashingmachineTime.postUpdate((now.millis - new DateTime(WashingmachineStartTime.state.toString).millis)/1000/60)
        WashingmachineEnergy.postUpdate(TasmotaSocketDryerEnergyTotal.deltaSince(new DateTime(WashingmachineStartTime.state.toString))  as Number)
    }    
    if (TasmotaSocketDryerEnergyPower.state < 1) WashingmachineState.postUpdate(MODE_OFF)
    else if (TasmotaSocketDryerEnergyPower.state > 10) { 
        WashingmachineState.postUpdate(MODE_ACTIVE) 
    }
    else if (TasmotaSocketDryerEnergyPower.state < 3) {
        if (WashingmachineState.state == MODE_OFF) WashingmachineState.postUpdate(MODE_STANDBY)
        else if (WashingmachineState.state == MODE_ACTIVE) {
            finishLock.lock()
            try {
                Thread::sleep(90000) 
                if (TasmotaSocketDryerEnergyPower.state < 3) { 
                    WashingmachineState.postUpdate(MODE_FINISHED)
                }
            } finally {
                finishLock.unlock()
            }
        } 
    }
end
 

rule "Washing Mashine State / Notifications / Tele Period Change"
when
	Item WashingmachineState changed
then 
    var String currentTime = String::format( "%1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS", new java.util.Date)
    val String currentHoursMinutes = String::format( "%1$tH:%1$tM", new java.util.Date)
 
	if (WashingmachineState.state == MODE_ACTIVE || WashingmachineState.state == MODE_STANDBY) {  
        if (WashingmachineState.state == MODE_ACTIVE) {
            //Resetting all values
            WashingmachineStartTime.postUpdate(now.toString)
            WashingmachineEndTime.postUpdate(NULL)
            WashingmachineTime.postUpdate(NULL) 
            WashingmachineEnergy.postUpdate(0)
            var message = "Washing started (ACTIVE): " + currentTime
            logInfo("householdDevices.rules", message)
            sendTelegram("bot", messagePrefix + message)
        }
        TasmotaSocketDryerTelePeriod.sendCommand(TELEPERIOD_ACTIVE)
        var message = "Tasmota Tele set to 30 (ACTIVE or STANDBY): " + currentTime
        logInfo("householdDevices.rules", message)
        sendTelegram("bot", messagePrefix + message)
    }   

	if (WashingmachineState.state == MODE_OFF) {  
        TasmotaSocketDryerTelePeriod.sendCommand(TELEPERIOD_OFF)
        var message = "Tasmota Tele set to 300 (OFF): " + currentTime
        logInfo("householdDevices.rules", message)
        sendTelegram("bot", messagePrefix + message)
    }
   
    timer = null
    repeatTimer = 60000
    
    timer = createTimer(now, [|   
        if (WashingmachineState.state == MODE_FINISHED) {
            WashingmachineEndTime.postUpdate(now.toString)
 
            timer.reschedule(now.plusMillis(repeatTimer.intValue))
            repeatTimer = ((repeatTimer as Number)* 2).intValue
            //logInfo("householdDevices.rules", "Washingmachine finished. Timer " + repeatTimer)
            
            var WashingmachineEndCounter = transform("JS", "duration.js", ((now.millis - new DateTime(WashingmachineEndTime.state.toString).millis)/1000/60).toString)
            
            var message = "Finished"
                          
            logInfo("householdDevices.rules", message)
            sendTelegram("bot", messagePrefix +  message)
           
        }
    ])

end

Sorry did not understood what do i need to do and how to solve it(((
Could you please tell.

Thanks!

You’ve given the full path to ReentrantLock. No import is needed.

Alternatively, use the import -

import java.util.concurrent.locks.ReentrantLock

var ReentrantLock finishLock = new ReentrantLock()
1 Like

I have to come in with the obligatory “this code has many antipatterns for Rules DSL.” See Why have my Rules stopped running? Why Thread::sleep is a bad idea for a full discussion.

  • ReentrantLocks are super dangerous to use in Rules DSL. There are cases where you cannot guarantee they get unlocked.

  • You only get five simultaneously running Rules. When you use ReentrantLocks and long sleeps you end up with Rules sitting around consuming a runtime thread waiting for access to the lock. In this case, if your Washingmachine Consumption Rule triggers five times in that minute and a half with a state of 3, none of your other Rules will be able to run for that time. If there is an error that bypasses the finally (which is possible in Rules DSL), none of your Rules will be able to run until OH restarts.

A far safer approach would be to use Timers instead of locks.

var Timer activeTimer = null

...

    if(TasmotaSocketDryerEnergyPower.state >= 3) {
        activeTimer.cancel()
        activeTimer = null
    }

    if (WashingmachineState.state == MODE_OFF) WashingmachineState.postUpdate(MODE_STANDBY)
    else if (WashingmachineState.state == MODE_ACTIVE) {
        if(activeTimer !== null && !activeTimer.hasTerminated) activeTimer.reschedule(now.plusSeconds(90))
        else {
            activeTimer = createTimer(now.plusSeconds(90), [ |
                WashingmachineState.postUpdate(MODE_FINISHED)
                activeTimer = null
            ]
        }
    }

The above does the same thing that your dangerous code appears to do (i.e. wait for the power to be under 3 for 90 seconds before setting the mode to MODE_FINISHED) without any risks to the rest of your Rules becoming unable to run.

1 Like

Thanks a lot!
I noticed that this rule works unstable, i thought it was problem with OH. But now i know it!
I got this script from mainterner ThomDietrich. Here is the post https://community.openhab.org/t/washing-machine-state-machine/15587

If this rule is not so good, it needs to be changed) I spent with it lot of time, thinking bad on OH, but the problem was very easy to solve, just change to timers)))

Thanks one more time!

Time passes, OH changes, and we learn more. That post was first published over three years ago.

1 Like