Smart Virtual Thermostat (beta version)

It might be useful to filter on those Items first and generate a warning if there is one or more Items with a null historic state. It would be a separate line before this one.

Hi, I’ve tried version 0.2.11 but still get a flood of exceptions:

2020-01-09 07:38:32.438 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.Timer 189 2020-01-09T07:38:32.436+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {

  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@19ffc97 (conditionalExpression: false)

} ] threw an unhandled Exception: 

java.lang.NullPointerException: null

	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:65) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:140) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:954) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:235) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:857) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:231) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1205) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1135) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:1081) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:237) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:469) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:255) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:458) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:239) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:201) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]

	at com.sun.proxy.$Proxy552.apply(Unknown Source) ~[?:?]

	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:48) ~[?:?]

	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [bundleFile:?]

	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [bundleFile:?]

2020-01-09 07:38:32.438 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.Timer 190 2020-01-09T07:38:32.438+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {

  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@19ffc97 (conditionalExpression: false)

} ] threw an unhandled Exception: 

java.lang.NullPointerException: null

	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:65) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:140) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:954) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:235) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:857) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:231) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1205) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1135) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:1081) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:237) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:469) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:255) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:458) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:239) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:201) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]

	at com.sun.proxy.$Proxy552.apply(Unknown Source) ~[?:?]

	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:48) ~[?:?]

	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [bundleFile:?]

	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [bundleFile:?]

2020-01-09 07:38:32.461 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.Timer 189 2020-01-09T07:38:32.436+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {

  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@19ffc97 (conditionalExpression: false)

} ] threw an exception.

org.quartz.SchedulerException: Job threw an unhandled exception.

	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [bundleFile:?]

	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [bundleFile:?]

Caused by: java.lang.NullPointerException

	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:65) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:140) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:954) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:235) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:857) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:231) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1205) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1135) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:1081) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:237) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:469) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:255) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:458) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:239) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:201) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]

	at com.sun.proxy.$Proxy552.apply(Unknown Source) ~[?:?]

	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:48) ~[?:?]

	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]

	... 1 more

2020-01-09 07:38:32.488 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.Timer 190 2020-01-09T07:38:32.438+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {

  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@19ffc97 (conditionalExpression: false)

} ] threw an exception.

org.quartz.SchedulerException: Job threw an unhandled exception.

	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [bundleFile:?]

	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [bundleFile:?]

Caused by: java.lang.NullPointerException

	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:65) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:140) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:954) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:235) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:857) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:231) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1205) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1135) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:1081) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:237) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:469) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:255) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:458) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:239) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:215) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:201) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]

	at com.sun.proxy.$Proxy552.apply(Unknown Source) ~[?:?]

	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:48) ~[?:?]

	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]

	... 1 more

If I can help you with adding some extra logging, just let me know and I’ll add

UPDATE 9 Jan 10:43 : After running for a few hours the exceptions seem to be gone now, only INFO statements are logged. I’ll monitor for a while to see how it behaves.
Thanks for your support already, much appreciated.

These null errors you can ignore. When you saved the rule file (I mean when openHAB reload the rule file), the existing timer items of the previous file still exists.
But when they run (timer went off) the environment in which they were created, doesn’t exist anymore.
With other word, they are ghost timers and thus creating errors of null.
If you look closely, the null expection is now at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:65) ~[?:?] instead of at org.eclipse.smarthome.model.persistence.extensions.PersistenceExtensions.historicState(PersistenceExtensions.java:163) ~[?:?]

Good idea!
This was for now a quick and dirty fix. If it is null it wil convert to 0. VSCode will give you a warning if you do the follow:

val temp = (null as Number)?.doubleValue

The warning is:
Null-safe call of primitive-valued feature doubleValue, default value 0 will be used

But it’s nicer to catch the null ad generate a warning as you suggested.

I also get some warnings when saving:

There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

However it doesn’t help where are these variables but I assume that there are Strings mainly where the type is not referred. I will try to clean them up…

I also made some cosmetic changes to the code (I think it is easier to read now for someone who is not that familiar with the code) if you like that I can create a PR on GitHub…

That’s strange, I’ve never seen these warnings. When/Where do you get them?

Yes, great, I’m trying to make to code available and readable for everyone, so be my guest and create a PR.

I get these warnings when I save the file.
Basically it happens because it can’t refer the return type from lambdas (for example when filtering for Items). I have tried adding the type to some of them (hope I got it right), but it I couldn’t find which 3 statement causes these.

2020-01-09 19:26:10.633 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'Thermostat.rules', using it anyway:

There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

2020-01-09 19:26:10.697 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'Thermostat.rules'

I only can find this part of code

Heating_Mode.allMembers.forEach[i|
					logInfo("svt2","retrigger {} with {}",i.name,i.state)
					i.sendCommand(i.state as Number)
					] 

Here i is unkown. There is a discusion here that talks about the same problem.
But strange enough, I’ve used the Design Pattern: Working with Groups in Rules post to create them and there is no mention of this problem in the example.

You were right, this is one of the three. I have added Number (before i) and I get one less warning.

I think the other 2 is where 2 implicit variable is used like:

val lastTemp = Temperatures.allMembers.filter[name.contains(room)].map[(historicState(new DateTime(lastCalcItem.state.toString),DATABASE)?.state as Number).doubleValue].reduce[s, v | s + v ] / Temperatures.allMembers.filter[name.contains(room)].size  //get average of room temperatures (group) at the last calc time

Here s and v are not referred with type…

Most of the time the Rules DSL is able to figure out what type i needs to be in that context. Sometimes it fails. This appears to be one of those cases. As Kristof indicates, the solution is to just provide the type before the i so the Rules DSL doesn’t have to try, and fail, to guess what type it is.

However, @rkrisi, the type of i is not a Number, it’s a NumberItem. Your change probably made the parse time error go away but it will fail at runtime.

Thanks :slight_smile: I already wanted to say that I’m not sure that the item type is correct, I just wanted to check this is what causes this…

After adding any type to these two (with a reduce where s, v is used) these warnings are gone. However I’m not sure what is the type of s and v there…
If you can help me out with the type I can include these fixes also in the PR (which I have created)

The result of the previous map is a List of Doubles. It would be far better to leave it as a Number than force it to be a primitive only to have it reconverted back to a Double (which is a Number) behind the scenes because you can’t store primitives in Lists. So s and v can be either Number or Double or probably even double would work.

But you should avoid the use of primitives as much as is possible. It greatly slows down the .rules file parsing. A few primitive references can add minutes on an RPi.

And I thought that using primitives are better in terms of resources :slight_smile: (primitive type vs a complete Object…).
I will give a Double for them I think.

In Rules DSL primitives are the worst. Really, in Rules DSL you should avoid defining types in general unless you have to. When you define the types you force the parser to do a whole lot more work than it would otherwise because it has to check to make sure all the types it can know about at parse time work with that type. And for some reason, using primitives causes the engine to do even more work.

Even on a very fast machine, there are some expressions that can take a couple of minutes to parse when you force it to use primitives and less than a second when you let the engine figure out the type on it’s own.

When you let the engine wait until runtime before it has to care about the types of the variables, it can skip all the type checking that takes places at parse time and at runtime it has a lot more information so the checks are really easy there too.

Good to know.
But I can’t find (or I’m not looking in the right docs) what I get with the doubleValue method.
For example:

var lastSetpoint = (lastSetpointState as Number).doubleValue

Will lastSetPoint become a double (primitive) or a Double (Object)?

A primitive double. Documentation is here.

Though in that case it might get converted back to a BigDecimal/Double/Number. But if you have

var double lastSetpoint = ...

it will be a primitive double.

As a general Rule, just don’t worry about type unless you have to. Let everything just be what it happens to be, or if you need to (e.g. like the state from a Number Item) cast it to Number, no more. Only call xValue when you absolutely have to (e.g. like in now.plusSeconds(lastSetpointState as Number).doubleValue)).

Edited the openingspost to reference to new versions of the files at GitHub.

In my Oppinion This idea is Great because it make devices independent from vendor.
What we Need is a value to Compare these rules against vendor bindings. I thought About valve Opening in %*seconds as sum for whole days. Let me think about it :slight_smile:

Okay,
i think i have an idea now:
i made some rules, that measure the usage of the thermostats.Note: In the Rules i do not address groups, but i plan to realize it due to simplyfing my code. Stay tuned.
What we need:

thermostat.items
Group gTemperatur_Ventil "Ventilöffnungen alle Heizungen" (gFF)
Group gTemperatur_Ventil_Persistence
Group gTemperatur_Ventil_Counter "Counter für Ventilöffnungen" (gFF,gTemperatur_Ventil_Persistence)
Group:Number:SUM gTemperatur_Ventil_Hourly "Counter für Ventilöffnungen daily" (gFF,gTemperatur_Ventil_Persistence)
Group:Number:SUM gTemperatur_Ventil_Daily "Counter für Ventilöffnungen daily" (gFF,gTemperatur_Ventil_Persistence)
Group:Number:SUM gTemperatur_Ventil_Weekly "Counter für Ventilöffnungen daily" (gFF,gTemperatur_Ventil_Persistence)
Group:Number:SUM gTemperatur_Ventil_Monthly "Counter für Ventilöffnungen daily" (gFF,gTemperatur_Ventil_Persistence)
//Groups
Number Wohnzimmer_Heizung_Links_Ventil_Counter "Zählt die Sekunden multipliziert mit der Öffnung entsprechend der Ventilöffnung" (gTemperatur_Ventil_Counter)
Number Wohnzimmer_Heizung_Links_Ventil_Counter_Daily "Zählt die Sekunden multipliziert mit der Öffnung entsprechend der Ventilöffnung" (gTemperatur_Ventil_Counter_Daily)
Number Wohnzimmer_Heizung_Links_Ventil_Counter_Hourly "Zählt die Sekunden multipliziert mit der Öffnung entsprechend der Ventilöffnung" (gTemperatur_Ventil_Counter_Hourly)
Number Wohnzimmer_Heizung_Links_Ventil_Counter_Weekly "Zählt die Sekunden multipliziert mit der Öffnung entsprechend der Ventilöffnung" (gTemperatur_Ventil_Counter_Weekly)
Number Wohnzimmer_Heizung_Links_Ventil_Counter_Monthly "Zählt die Sekunden multipliziert mit der Öffnung entsprechend der Ventilöffnung" (gTemperatur_Ventil_Counter_Monthly)
thermostat.rules
rule "WZ Heizung links an"
when
        Member of gTemperatur_Ventil changed
then
        val devicename = triggeringItem.name.toString
        val devicename_Counter = devicename + "_Counter"
        val devicename_Counter_item = gTemperatur_Ventil_Counter.members.findFirst[ d | d.name == devicename_Counter ]
                val lastOpen = triggeringItem.previousState(true).timestamp.time
                if(triggeringItem.previousState(true).state!=0){
                        logInfo("Valve Opened", "Ventil letzter Change: " + lastOpen)
                        logInfo("Valve Opened", "Ventil now: " + now + " timestamp: " + now.millis)
                        val totalTime = now.millis - lastOpen
                        val totalTimeSec = totalTime / 1000
                        val ValveState = triggeringItem.previousState(true).state as DecimalType
                        val ValveStatePercent = ValveState * 0.01
                        val heatingCounter = ValveStatePercent * totalTimeSec
                        postUpdate(devicename_Counter_item,heatingCounter)
                        logInfo("Valve Opened", "Ventil war für X sekunden offen: " + triggeringItem.name + " Sekunden: " + totalTimeSec + " heatingcounter: " + heatingCounter +$
                }
end

rule "Heizungen Verbraucherstatistik - hourly"
when
        Time cron "0 0 0/1 1/1 * ? *"
then
        logInfo("Valve Hourly", "Hourly Ventilstatus schreiben")
        postUpdate(Wohnzimmer_Heizung_Links_Ventil_Counter_Hourly,Wohnzimmer_Heizung_Links_Ventil_Counter.sumSince(now.minusHours(1),"jdbc"))
        postUpdate(Wohnzimmer_Heizung_Rechts_Ventil_Counter_Hourly,Wohnzimmer_Heizung_Rechts_Ventil_Counter.sumSince(now.minusHours(1),"jdbc"))
        postUpdate(Kinderzimmer_Heizung_Ventil_Counter_Hourly,Kinderzimmer_Heizung_Ventil_Counter.sumSince(now.minusHours(1),"jdbc"))
        postUpdate(Schlafzimmer_Heizung_Ventil_Counter_Hourly,Schlafzimmer_Heizung_Ventil_Counter.sumSince(now.minusHours(1),"jdbc"))

end

rule "Heizungen Verbraucherstatistik - daily"
when
        Time cron "0 0 0 1/1 * ? *"
then
        logInfo("Valve Hourly", "Daily Ventilstatus schreiben")
        postUpdate(Wohnzimmer_Heizung_Links_Ventil_Counter_Daily,Wohnzimmer_Heizung_Links_Ventil_Counter.sumSince(now.minusHours(24),"jdbc"))
        postUpdate(Wohnzimmer_Heizung_Rechts_Ventil_Counter_Daily,Wohnzimmer_Heizung_Rechts_Ventil_Counter.sumSince(now.minusHours(24),"jdbc"))
        postUpdate(Kinderzimmer_Heizung_Ventil_Counter_Daily,Kinderzimmer_Heizung_Ventil_Counter.sumSince(now.minusHours(24),"jdbc"))
        postUpdate(Schlafzimmer_Heizung_Ventil_Counter_Daily,Schlafzimmer_Heizung_Ventil_Counter.sumSince(now.minusHours(24),"jdbc"))
end

rule "Heizungen Verbraucherstatistik - weekly"
when
        Time cron "0 0 0 ? * MON *"
then
        logInfo("Valve Hourly", "Daily Ventilstatus schreiben")
        postUpdate(Wohnzimmer_Heizung_Links_Ventil_Counter_Daily,Wohnzimmer_Heizung_Links_Ventil_Counter.sumSince(now.minusHours(168),"jdbc"))
        postUpdate(Wohnzimmer_Heizung_Rechts_Ventil_Counter_Daily,Wohnzimmer_Heizung_Rechts_Ventil_Counter.sumSince(now.minusHours(168),"jdbc"))
        postUpdate(Kinderzimmer_Heizung_Ventil_Counter_Daily,Kinderzimmer_Heizung_Ventil_Counter.sumSince(now.minusHours(168),"jdbc"))
        postUpdate(Schlafzimmer_Heizung_Ventil_Counter_Daily,Schlafzimmer_Heizung_Ventil_Counter.sumSince(now.minusHours(168),"jdbc"))
end
thermostat.sitemap
        Frame                   label="Heizungsverbrauch"
        {
                Text item=Wohnzimmer_Heizung_Links_Ventil_Counter_Hourly label="Hourly von WZ Heizung Links: [%s]"
                Text item=Wohnzimmer_Heizung_Rechts_Ventil_Counter_Hourly label="Hourly von WZ Heizung Rechts: [%s]"
                Text item=Kinderzimmer_Heizung_Ventil_Counter_Hourly label="Hourly von Kinderzimmerheizung: [%s]"
                Text item=Schlafzimmer_Heizung_Ventil_Counter_Hourly label="Hourly von Schlafzimmerheizung: [%s]"
                Text item=Wohnzimmer_Heizung_Links_Ventil_Counter_Daily label="Daily von WZ Heizung Links: [%s]"
                Text item=Wohnzimmer_Heizung_Rechts_Ventil_Counter_Daily label="Daily von WZ Heizung Rechts: [%s]"
                Text item=Kinderzimmer_Heizung_Ventil_Counter_Daily label="Daily von Kinderzimmerheizung: [%s]"
                Text item=Schlafzimmer_Heizung_Ventil_Counter_Daily label="Daily von Schlafzimmerheizung: [%s]"
        }
}```

How does it work?
the Rule calculates the seconds how long the valve did not stay at 0%. This value is multiplied with the percentage the value stood open. This way you get a rating that is updated to a counter. With the persistence rules you are able to display the usage for a period.

What can you do with all that values now? You can compare them to values that were generated from vendor solutions i.e. Homematic. Therefore you can see how many heating is needed to fulfill the requirements.
What is missing? I will implement a Method, that will measure the Delta over time to the Target temperature. This way you can measure the effectivity of your ruleset compared to other mechanisms.
For Example.
If you have a low valveusage and a low delta to target temperature , you can say that your method works pretty well. When a homematic solution has much worse values you can be glad :slight_smile:

Best regards