Another timer, or two - how to use them correctly Rule DSL

Hello world,
I am trying to make my Smart Grid heat pump smarter. I need to turn off the heating to really start the promised SG function. Therefore, I have added a MQTT relay to the Heat Pump. I need to switch it On or Off it depending on Total exported energy produced from my Photovoltaics. Here, for test purposes it is Import instead of Export.

The principle:

/*if live import is >2 min, >1kW
AlthermaOnOff_state ON
if live import is <0.8kW, >5min
AlthermaOnOff_state OFF*/

var Timer myTimerOn = null
var Timer myTimerOff = null

rule "altherma_sg"
when
    Item SolarEdge_Live_Import changed
then
    logInfo("altherma_sg.rules", "Rule altherma_sg.rules running")
    if(SolarEdge_Live_Import.state > 1|kW) {  
        logInfo("altherma_sg.rules", "More than 1kW, waiting 2 min")
        myTimerOn = createTimer(now.plusMinutes(2), [ |
        logInfo("altherma_sg.rules", "running test, if still more than 1kW")
        if(SolarEdge_Live_Import.state > 1|kW) {
            logInfo("altherma_sg.rules", "still more than 1kW")
            AlthermaOnOff_state.sendCommand(ON)
            logInfo("altherma_sg.rules", "SG ON, resetting myTimerOn")
            myTimerOn = null
        }
        else if(SolarEdge_Live_Import.state < 0.8|kW) {
            logInfo("altherma_sg.rules", "Less than 0.8kW, rescheduling timer +3 min")
            myTimerOff = createTimer(now.plusMinutes(3), [ |
                logInfo("altherma_sg.rules", "myTimerOff 3 min activated")
                AlthermaOnOff_state.sendCommand(OFF)
                logInfo("altherma_sg.rules", "SG OFF, resetting myTimerOff")
                myTimerOff = null
            ])
        }
        ])
    }
end

I red this: https://www.openhab.org/docs/configuration/actions.html#timers but it seems that I did not get it properly, as OH3 is throwing these errors:

2021-02-16 00:40:51.983 [WARN ] [ore.internal.scheduler.SchedulerImpl] - Scheduled job failed and stopped
java.lang.reflect.UndeclaredThrowableException: null
	at com.sun.proxy.$Proxy713.apply(Unknown Source) ~[?:?]
	at org.openhab.core.model.script.actions.ScriptExecution.lambda$0(ScriptExecution.java:82) ~[?:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$12(SchedulerImpl.java:166) ~[bundleFile:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$1(SchedulerImpl.java:76) [bundleFile:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:834) [?:?].............

Thank you,
Michal

There is an error inside the body of your Timer. What the error is is hard to say. It’s a complicated set of nested timers and you’ve not properly indented them (it’s only by luck that I noticed they were nested actually.

However, does this occur every time or only when you reload the .rules file? If only when reloading the .rules file this just means that all the Timers that were created in that file have become orphaned and they throw errors when they finally run. There isn’t really anything you can do about that in Rules DSL.

You might consider how often that changes. Every time it does, you fire off a new timer regardless of if one already exists, and trample over the handle variable for the previous timer.

@rlkoshak
The script should be running 24/7 (or at least from dusk to dawn), checking the electricity values and switching On/Off the relay.
Does it mean I need to use another scripting language?

@rossko57, every 2 minutes - the polling interval of solar invertor. This will be probably the root of problems!

@both:
firstly I was defining when with

when
    Item SolarEdge_Live_Import changed to >1|kW

but this does not work (tried more combinations) and as I get it, I cant activate the Rule this way


No,it means if you edit the rules file that started off a long-running timer while the timer is running,it is likely to generate an error like the one you showed us at its appointed time. (It’s because its context has been lost.)

It’s not a problem unless you edit rules all the time and are surprised by the message.

Okay, you will see in the many available timer examples the common trickof testing your handle variable to see if it is null-it’s a way to see if a timer is already running, as you’ve already set the handle variable to be a valid handle when it is running, and set to null when it ends.
Then you can make a decision to let it run, reschedule,or cancel.

So the when - script starter should not be my Item SolarEdge_Live_Import changed, because then the script is launched all the time the value is changed. It can be started with lets say sunrise, and ends with sunset.
But then I need to make it start over again manually (button?) to test it.

Here the improved script,

  • Added launch with sunrise or button

  • I made some indent, as @rlkoshak recommended

  • Added 2x timer resetter

  • I suppose, that var Timer myTimerOn = null resets the timer every time Rule newly ran (with button, or astro).

      /*if live import is >1kW, >2min 
      AlthermaOnOff_state ON
      if live import is <0.8kW, >5min
      AlthermaOnOff_state OFF*/
      var Timer myTimerOn = null
      var Timer myTimerOff = null
    
      rule "altherma_sg"
      when
          Channel "astro:sun:local:rise#end" triggered START or
          Channel "mihome:sensor_switch:286c0785f092:158d0001243994:button" triggered "LONG_PRESSED"
      then
          logInfo("altherma_sg.rules", "Rule altherma_sg.rules running")
          if(SolarEdge_Live_Import.state > 1|kW) {  
              logInfo("altherma_sg.rules", "More than 1kW, waiting 2 min")
              myTimerOn = createTimer(now.plusMinutes(2), [ |
                  logInfo("altherma_sg.rules", "running test, if still more than 1kW")
                  if(SolarEdge_Live_Import.state > 1|kW) {
                      logInfo("altherma_sg.rules", "still more than 1kW")
                      AlthermaOnOff_state.sendCommand(ON)
                      logInfo("altherma_sg.rules", "SG ON, resetting myTimerOn, resetting myTimerOff")
                      myTimerOn = null
                      myTimerOff = null
                  }
              ])
          }
          else if(SolarEdge_Live_Import.state < 0.8|kW) {
              logInfo("altherma_sg.rules", "Less than 0.8kW, rescheduling timer +3 min")
              myTimerOff = createTimer(now.plusMinutes(3), [ |
                  logInfo("altherma_sg.rules", "myTimerOff 3 min activated")
                  AlthermaOnOff_state.sendCommand(OFF)
                  logInfo("altherma_sg.rules", "SG OFF, resetting myTimerOff, resetting myTimerOn")
                  myTimerOff = null
                  myTimerOn = null
              ])
          }
      end
    
  • Now the rule runs until first if, then i need to press button to run it again to next if then again the button to continue. Why? because of rule trigger set to astro/button? Isn’t the rule resident in memory now?

What’s wrong with that? It’s quite usual to run a rule at each change, and the do something or not based on conditions at the time. That’s how event driven systems work.

This has nothing to do with editing rules to make orphan timers by accident.

Your trigger at the moment runs the rule once only at sunrise, and examines power at that instant (won’t that be tiny?)
 That’s it,until tomorrow.
Not sure what you are trying to achieve.

No.
myTimerOn is just a handle, a pointer,to a fully independent timer. You can set it to null or to "bananas" and it does not affect the timer at all. But you will have lost any way to communicate with the timer.
If you don’t destroy it prematurely, you can use it to give instructions to the timer,like
myTimerOn.cancel()
If you set this handle to null when you really have finished with the timer, you have created a way for a rule to detect if your timer is already running or not.

Your rule is still blindly creating timers that may already exist, and in doing so trampling over their handles.
Please look at simple timer examples that check to see if the handle is null or not before creating a new timer, and can then cancel or reschedule instead.

1 Like

I red the manual about timers again and again.

It should do this:
If Import is more than 1 kw,
timer for 2 mins,
if Import is still more than 1kw
AlthermaOnOff ON

If Import is less than 0,8 kw,
timer for 3 mins,
if Import is still more than 0,8kw
AlthermaOnOff OFF

I ended with two rules, one for ON, one for OFF
In every rule, there is one unique timer.

var Timer myTimerOn = null

rule "altherma_sgridOn"
when
    Item SolarEdge_Live_Import changed
then
    logInfo("altherma_sgridOn.rules", "Rule altherma_sgridOn.rules running")
    if (SolarEdge_Live_Import.state > 1|kW) {  
        logInfo("altherma_sgridOn.rules", "More than 1kW")
        if (myTimerOn !== null) {
            logInfo("altherma_sgridOn.rules", "myTimerOn rescheduled")
            myTimerOn.reschedule(now.plusMinutes(2))
        }
        else {
            myTimerOn = createTimer(now.plusMinutes(2), [ |
                logInfo("rules", "myTimerOn activated")
                if (SolarEdge_Live_Import.state > 1|kW) {
                    logInfo("altherma_sgridOn.rules", "still more than 1kW")
                    AlthermaOnOff_state.sendCommand(ON)
                    logInfo("altherma_sgridOn.rules", "SG ON")
                }
            ])
            logInfo("rules", "myTimerOn created")
        }
    }
    else {
        logInfo("rules", "myTimerOn canceled")
        myTimerOn.cancel()
        myTimerOn = null
    }
end

OFF with 5 min timer

var Timer myTimerOff = null

rule "altherma_sgridOff"
when
    Item SolarEdge_Live_Import changed
then
    logInfo("altherma_sgridOff.rules", "Rule altherma_sgridOff.rules running")
    if (SolarEdge_Live_Import.state < 0.8|kW) {  
        logInfo("altherma_sgridOff.rules", "Less than 0.8kW")
        if (myTimerOff !== null) {
            logInfo("altherma_sgridOff.rules", "myTimerOff rescheduled")
            myTimerOff.reschedule(now.plusMinutes(3))
        }
        else {
            myTimerOff = createTimer(now.plusMinutes(3), [ |
                logInfo("altherma_sgridOff", "myTimerOff activated")
                if (SolarEdge_Live_Import.state < 0.8|kW) {
                    logInfo("altherma_sgridOff.rules", "still less than 0.8kW")
                    AlthermaOnOff_state.sendCommand(OFF)
                    logInfo("altherma_sgridOff.rules", "SG OFF")
                }
            ])
            logInfo("altherma_sgridOff", "myTimerOff created")
        }
    }
    else {
        logInfo("altherma_sgridOff", "myTimerOff canceled")
        myTimerOff.cancel()
        myTimerOff = null
    }
end

but sometimes I have some error messages, which I can’t solve (even when I have used exactly the solution from tutorial example):

2021-02-17 21:46:54.558 [INFO ] [.core.model.script.altherma_sgridOff] - myTimerOff activated
2021-02-17 21:46:54.566 [INFO ] [model.script.altherma_sgridOff.rules] - still less than 0.8kW
2021-02-17 21:46:54.571 [INFO ] [model.script.altherma_sgridOff.rules] - SG OFF
2021-02-17 21:46:54.574 [INFO ] [model.script.altherma_sgridOff.rules] - Rule altherma_sgridOff.rules running
2021-02-17 21:46:54.575 [INFO ] [.model.script.altherma_sgridOn.rules] - Rule altherma_sgridOn.rules running
2021-02-17 21:46:54.583 [INFO ] [model.script.altherma_sgridOff.rules] - Less than 0.8kW
2021-02-17 21:46:54.584 [INFO ] [org.openhab.core.model.script.rules ] - myTimerOn canceled
2021-02-17 21:46:54.587 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'altherma_sgridOn-1' failed: cannot invoke method public abstract boolean org.openhab.core.model.script.actions.Timer.cancel() on null in altherma_sgridOn
2021-02-17 21:46:54.595 [INFO ] [model.script.altherma_sgridOff.rules] - myTimerOff rescheduled
2021-02-17 21:46:54.603 [WARN ] [el.script.internal.actions.TimerImpl] - Rescheduling failed as execution has already started!

I think it should be later doable with 1 timer only, but step by step


Thanks for patience,
Michal

There are times when the rule gets triggered, SolarEdge_Live_Import.state < 1|kW, and myTimerOn is null. In other words, the else part of the rule runs before a timer was ever created. You have to check that a timer actually exists before calling cancel() because null doesn’t have a cancel() method.

There is a shortcut in Rules DSL to do this.

myTimerOn?.cancel()

That is equivalent to

if(myTimerOn !== null) {
    myTimerOn.cancel()
}
2 Likes

Thanks, now it works without errors.

My logic is bad. I see it now. The value SolarEdge_Live_Import is changing every 2 minutes. That means, the command is running every 2 minutes too. If the command will find that the SolarEdge_Live_Import.state < 1|kW, it will run from beginning. after 2 minutes - again, from beginning.
There, I need a blocking timer, right?

I have learned something about timers, and now, it ends so simple! :slight_smile:

Maybe the tutorial here needs to be changed too?

It’s not the coding, it’s setting out for yourself what you want to happen
What’s missing from your outline above-

What would you like to happen if power changes and one or other timer is already running? There are options to leave timer running, extend it, or abort it and do something else, etc.

What would you like to happen if its already been off for a while? Keep starting up timers to turn it off again seems likely unwanted.
Same for on for a while.
(You’d probably have your rule check the existing pump state)

1 Like

Thanks, so I have added to if: && AlthermaOnOff_state.state == OFF and one else if (never used it before!) with SolarEdge_Live_Import.state > 1|kW && AlthermaOnOff_state.state == ON

even the not actual timer is now cancelled.

var Timer myTimerOn = null
val float onkW = 1
val int onMinutes = 2

rule "altherma_sgridOn"
when
    Item SolarEdge_Live_Import changed
then
    logInfo("altherma_sgridOn.rules", "Rule altherma_sgridOn.rules running")
    if (SolarEdge_Live_Import.state > 1|kW && AlthermaOnOff_state.state == OFF) {    // && AlthermaOnOff_state.state == OFF && AlthermaOnOff_state.state == ON ???
        logInfo("altherma_sgridOn.rules", "More than 1kW & Altherma Off. Lets do something")
        if (myTimerOn !== null) {
            logInfo("altherma_sgridOn.rules", "myTimerOn rescheduled")
            myTimerOn.reschedule(now.plusMinutes(2))
        }
        else {
            myTimerOn = createTimer(now.plusMinutes(2), [ |
                logInfo("rules", "myTimerOn activated")
                if (SolarEdge_Live_Import.state > 1|kW) {
                    logInfo("altherma_sgridOn.rules", "still more than 1kW")
                    AlthermaOnOff_state.sendCommand(ON)
                    logInfo("altherma_sgridOn.rules", "SG ON, canceling myTimerOff")
                    myTimerOff?.cancel()  //if myTimerOff is running, cancel it
                    myTimerOff = null  
                }
            ])
            logInfo("altherma_sgridOn.rules", "myTimerOn created")
        }
    }
    else if(SolarEdge_Live_Import.state > 1|kW && AlthermaOnOff_state.state == ON ) {
        logInfo("altherma_sgridOn.rules", "More than 1kW and Altherma already ON. Lazy days.")
    }
    else {
        logInfo("altherma_sgridOn.rules", "myTimerOn canceled")
        myTimerOn?.cancel()
        myTimerOn = null
    }
end

the other one:

var Timer myTimerOff = null
val float offkW = 0.80
val int offMinutes = 3

rule "altherma_sgridOff"
when
    Item SolarEdge_Live_Import changed
then
    logInfo("altherma_sgridOff.rules", "Rule altherma_sgridOff.rules running")
    if (SolarEdge_Live_Import.state < 0.80|kW && AlthermaOnOff_state.state == ON) {  // && AlthermaOnOff_state.state == ON
        logInfo("altherma_sgridOff.rules", "Less than 0.8kW & Altherma On. Lets do something")
        if (myTimerOff !== null) {
            logInfo("altherma_sgridOff.rules", "myTimerOff rescheduled")
            myTimerOff.reschedule(now.plusMinutes(3))
        }
        else {
            myTimerOff = createTimer(now.plusMinutes(3), [ |
                logInfo("altherma_sgridOff.rules", "myTimerOff activated")
                if (SolarEdge_Live_Import.state < 0.80|kW) {
                    logInfo("altherma_sgridOff.rules", "still less than 0.8kW")
                    AlthermaOnOff_state.sendCommand(OFF)
                    logInfo("altherma_sgridOff.rules", "SG OFF, canceling myTimerOn, if running")
                    myTimerOn?.cancel() //if myTimerOn is running, cancel it
                    myTimerOn = null    
                }
            ])
            logInfo("altherma_sgridOff.rules", "myTimerOff created")
        }
    }
    else if( SolarEdge_Live_Import.state < 0.8|kW && AlthermaOnOff_state.state == OFF ) {
        logInfo("altherma_sgridOff.rules", "Less than 0.8kW & Altherma already Off. Lazy days.")
    }
    else {
        logInfo("altherma_sgridOff.rules", "myTimerOff canceled")
        myTimerOff?.cancel()
        myTimerOff = null
    }
end

Ill test the scripts for a while, but it seems it is working?

I defined a values at the beginning, but if I surpress in the code lets say the 0.8 with value defined on the beginning offkW, it is not accepted (tail). val float offkW = 0.80 throws this error:

`2021-02-19 00:32:04.862 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'altherma_sgridOff.rules' has errors, therefore ignoring it: [11,45]: no viable alternative at input '|'

[11,46]: missing ')' at 'kW'

[11,83]: mismatched input ')' expecting 'end'` 

Can you please help me why?

And 
 I think i still need to define what will be done in the interval between 0.8 and 1. Here it comes, I will probably merge the rules again :smiley:

EDIT:
The timer is not running (probably?), it looks like all the conditions of else if(SolarEdge_Live_Import.state < 0.80|kW && AlthermaOnOff_state.state == OFF) has been met and I got this?

2021-02-19 00:56:56.585 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'SolarEdge_Live_Import' changed from 0.83 kW to 0.79 kW
2021-02-19 00:58:56.613 [INFO ] [model.script.altherma_sgridOff.rules] - myTimerOff canceled

Something is wrong then. Or is the SolarEdge_Live_Import.state taking the 0.79 kW instead of 0.83 kW value?

Thanks,
Michal

This is going to be one of those daft DSL quirks.
It works out what an operator like < is supposed to do based on preceding object. “+” for an example does something very different for strings as opposed to numbers.
So it looks at SolarEdge_Live_Import.state for a clue, but it’s a state type object and that doesn’t help here.
< is especially hard to guess, because it just might be one of a pair like <value>. I think this is what causes the whining, as with no other clue it goes on to look at 8|kW) looking for the >.

Make it a type where < has meaning

if ( (SolarEdge_Live_Import.state as QuantityType<Number>) < 0.80|kW && ...

Now it knows it’s a numeric Quantity and < means “less than” (after any units conversions).

1 Like

Thanks again @rossko57,

I used it like you wrote. Here the OFF code (I changed the Import to Consumption, as it is easier for simulating changing of values):

var Timer myTimerOff = null
val float offkW = 0.80  // thresold to switch on
val int offMinutes = 3  // how long to watch the onkW value before switch on

rule "altherma_sgridOff"
when
    Item SolarEdge_Live_Consumption changed
then
    logInfo("altherma_sgridOff.rules", "Rule altherma_sgridOff.rules running")
    if ((SolarEdge_Live_Consumption.state as QuantityType<Number>) < 0.8|kW && AlthermaOnOff_state.state == ON) {  // && AlthermaOnOff_state.state == ON
        logInfo("altherma_sgridOff.rules", "Less than 0.8kW & Altherma On. Lets do something")
        if (myTimerOff !== null) {
            logInfo("altherma_sgridOff.rules", "myTimerOff rescheduled")
            myTimerOff.reschedule(now.plusMinutes(offMinutes))
        }
        else {
            myTimerOff = createTimer(now.plusMinutes(offMinutes), [ |
                logInfo("altherma_sgridOff.rules", "myTimerOff activated")
                if ((SolarEdge_Live_Consumption.state as QuantityType<Number>) < 0.80|kW) {
                    logInfo("altherma_sgridOff.rules", "still less than 0.8kW")
                    AlthermaOnOff_state.sendCommand(OFF)
                    logInfo("altherma_sgridOff.rules", "SG OFF, canceling myTimerOn, if running")
                    myTimerOn?.cancel() //if myTimerOn is running, cancel it
                    // myTimerOn = null    
                }
            ])
            logInfo("altherma_sgridOff.rules", "myTimerOff created")
        }
    }
    else if((SolarEdge_Live_Consumption.state as QuantityType<Number>) < 0.80|kW && AlthermaOnOff_state.state == OFF) {
        logInfo("altherma_sgridOff.rules", "Less than 0.8kW & Altherma already Off. Lazy days.")
    }
    else {
        logInfo("altherma_sgridOff.rules", "myTimerOff canceled")
        myTimerOff?.cancel()
        myTimerOff = null
    }
end

I got still bug in my code - it seems that it is not taking the the right value 0.79 should be the limit IMHO:

2021-02-19 20:44:56.395 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'SolarEdge_Live_Consumption' changed from 0.8 kW to 0.81 kW
2021-02-19 20:46:56.558 [INFO ] [.model.script.altherma_sgridOn.rules] - Rule altherma_sgridOn.rules running
2021-02-19 20:46:56.560 [INFO ] [model.script.altherma_sgridOff.rules] - Rule altherma_sgridOff.rules running
2021-02-19 20:46:56.576 [INFO ] [model.script.altherma_sgridOff.rules] - Less than 0.8kW & Altherma On. Lets do something
2021-02-19 20:46:56.578 [INFO ] [.model.script.altherma_sgridOn.rules] - myTimerOn canceled
2021-02-19 20:46:56.584 [INFO ] [model.script.altherma_sgridOff.rules] - myTimerOff created

Even there is a warning sometimes, I though it is made by myTimerOn = null in the script, there I commented it out, but it is still there:

2021-02-19 20:52:16.185 [INFO ] [model.script.altherma_sgridOff.rules] - SG OFF, canceling myTimerOn, if running
2021-02-19 20:52:16.187 [WARN ] [ore.internal.scheduler.SchedulerImpl] - Scheduled job failed and stopped
java.lang.reflect.UndeclaredThrowableException: null
	at com.sun.proxy.$Proxy713.apply(Unknown Source) ~[?:?]
	at org.openhab.core.model.script.actions.ScriptExecution.lambda$0(ScriptExecution.java:82) ~[?:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$12(SchedulerImpl.java:166) ~[bundleFile:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$1(SchedulerImpl.java:76) [bundleFile:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: org.openhab.core.model.script.engine.ScriptExecutionException: The name 'myTimerOn' cannot be resolved to an item or type; line 24, column 21, length 9
	at org.openhab.core.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:141) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:955) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:236) ~[?:?]
	at org.openhab.core.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:858) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:232) ~[?:?]
	at org.openhab.core.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:459) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:240) ~[?:?]
	at org.openhab.core.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:472) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:256) ~[?:?]
	at org.openhab.core.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:459) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:240) ~[?:?]
	at org.openhab.core.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:202) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:47) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:30) ~[?:?]
	... 10 more

At the beginning, it looks like a simple thing :grimacing:

Perhaps AlthermaOnOff_state.state is not what you expect, log it out to see.

Make a little test rule for a sanity check.

rule "sanity check"
when
    Item SolarEdge_Live_Consumption changed
then
    if ((SolarEdge_Live_Consumption.state as QuantityType<Number>) < 0.8|kW) {
        logInfo("test", "Less than 0.8kW")
   } else {
        logInfo("test", "Not less than 0.8kW")
   }
    if ((SolarEdge_Live_Consumption.state as QuantityType<Number>) > 0.8|kW) {
        logInfo("test", "More than 0.8kW")
   } else {
        logInfo("test", "Not more than 0.8kW")
   }
end

I was always wrong reading the log. First it is listed the rule, then after it, the SolarEdge_Live_Consumption.state ! I was blind all the time, but thanks to your sanity check, which is actually working perfectly, I saw it!

What about the error displayed from time to time? It appears, when the timer should be cancelled.
Am I doing it correctly?

            myTimerOff?.cancel()   //if myTimerOff is running, cancel it
            myTimerOff = null

this log is pointing to the first line - is the myTimerOff a type? It is not an Item I hope:

Caused by: org.openhab.core.model.script.engine.ScriptExecutionException: The name 'myTimerOff' cannot be resolved to an item or type; line 27, column 21, length 10

It’s the kind of error you get if the variable has not previously been defined.
But how could that be, you defined it as a global at the top of the file?
Hmm, but you’re using two files for some reason - and each file has only one of the timer handle variables. But - each rule refers to the other timer.

The “globals” only exist within the scope of the one xxx.rules file. While they can be shared between any rule in the file - they are not truly global across rules from other files.
Put both rules in one file, unify your collection of globals at the top.

1 Like

Done. Ill test it for a while, but it runs for 50 minutes with no bug. I have added also another if for interval >0.8 and <1 with logInfo message to see the interval better.

i still did not found how to change the constant > 1|kW in the code to val float onkW = 1 defined at beginning. > onkW|kW does not work.

2021-02-19 00:32:04.862 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'altherma_sgridOff.rules' has errors, therefore ignoring it: [11,45]: no viable alternative at input '|'
[11,46]: missing ')' at 'kW'
[11,83]: mismatched input ')' expecting 'end'` 

Is there a trick to do it? Just to make the code nicer.

Just funt fact:
Today I made my first video for other group how does it work now. https://www.youtube.com/watch?v=fkAtnWUb_fo&t One relay will be instead of thermostat, switching the heating off, and I am planning to replace the SolarEdge Dry contact switch later with second relay working with the same rules steering the LAN Adaptor.

No, it wouldn’t. The pipe | character only works its magic on constants.
Make your variable a Quantity type in the first place, using the magic.

val myThreshold = 0.8|kW
// rules interpreter generally works out type for itself
...
// later
if ((SolarEdge_Live_Consumption.state as QuantityType<Number>) < myThreshold) {
1 Like

Ok, it worked.
The code is now running smoothly, with no issues at all. After I will change the variables and Items, I will be able to use it to steer my Heatpump.

Thank you for you patience and your help!!! I learned lot of new things during this session.
Michal, Slovakia

1 Like