How to define global variables in oh3 for DSL rule in main UI

Hello,

I transfer my DSL rules into oh3 and I will use main UI for it. I have a rule with a lambda expression and for this I need to define a global variables. Where I have to do this, I have no clue… Can somebody give me a hint.

Thanks in advanced.

Johannes

1 Like

There are several recent threads about this on this forum. None of them contain good news - your simplest option is to keep your DSL rules in xxx.rules files, which will work as before.

Thank you! I hoped I could use the UI…

So this is really more of an XY Problem. You currently implement something in a global lambda, but is that the best approach to achieve what you are trying to with that lambda? In my experience the answer is probably not. Global lambdas are a code smell to me and there are only a few specific circumstances where they should be used.

Having said that, there might be other options. For example, Design Pattern: Separation of Behaviors. Another approach could be to put the code into a Script and use the “Other Rules -> Run another rule” to call it. Maybe this is a good opportunity to start exploring one of the other supported languages where you can have libraries (OH 3 Examples: Writing and using JavaScript Libraries in MainUI created Rules).

1 Like

Hello @rlkoshak,

here you can see my old oh2 rule:

rule “Fenster Info Abwesenheit”

when

Item Anwesenheit changed from ON to OFF //or

then
val telegramAction = getActions(“telegram”,“telegram:telegramBot:49b7f025cb”)
if (gWindowsfind.state == OPEN) {
var String open_windows_read
var String open_windows1 = “Offene Fenster/Türen: \n”
gWindowsfind.members.forEach [ window |
if (window.state == OPEN) {
open_windows_read = (transform(“MAP”, “fenster.map”, window.name))
open_windows1 = open_windows1 +"-"+ open_windows_read + " \n"
}
]
telegramAction.sendTelegram(""+ open_windows1 +"")
end

If I use this rule inside a rule file in oh3 everthing is ok and runs without any error, as rule in the main UI I get the following error:

  1. Cannot refer to the non-final variable open_windows_read inside a lambda expression; line 12, column 475, length 17

  2. Cannot refer to the non-final variable open_windows1 inside a lambda expression; line 13, column 562, length 13

  3. Cannot refer to the non-final variable open_windows1 inside a lambda expression; line 13, column 578, length 13

  4. Cannot refer to the non-final variable open_windows_read inside a lambda expression; line 13, column 598, length 17

I found a thread in the community where the info was given to declare the variable as global variable to solve the problem.
But I think you are right, its time to go a step deeper in the new languages, but first I would migrate my system to oh3 and then I will start to improve my rules.

You should be getting the error in .rules files too. It’s always been the case that you cannot modify a var inside a forEach.

If you move the open_windows_read declaration inside the forEach and switch to using a StringBuilder val it will work just fine. Or you could use a map/reduce.

val openWindowNames = gWindoesfind.members
                                  .filter[ window | window.state == OPEN]
                                  .map[ window | transform("MAP", "fenster.map", window.name) ]
                                  .reduce[ lst, name | lst = lst + "-"+name+"\n" ]
val open_windows1 = "Offene Fenster//Türen: \n"+openWindowNames

NOTE: I’m typing this in from memory, the lst and name in the reduce might be backwards.

1 Like

Thank You Rick! I have tried your approach but I get the following error according to .reduce expression:

Assignment to final parameter; line 7, column 362, length 6

I have checked your Design Pattern Working with Groups in rules but I can not find the failure. Your mentioned the expression could be backwards but in my opinion it is correct.

Can you please point me in the right direction?

You don’t need the assignment in the reduce.

.reduce[ lst, name | lst + "-" + name + "\n" ]

Hello @rlkoshak,

actually I try to rebuild my rules to ECMA rules in the ui, but I don’t find a solution to port the quoted expression to javascript. Please can you give me a hint?

Thank you

I haven’t had time to update the Working with Groups in Rules DP with JavaScript yet.

You have two options. You can convert the list returned by members to a native JavaScript array and use the stuff built into JavaScript to do the operations. But openHAB has Nashorn as the JS engine which is ECMAScript 5.1 and lacks some important features like filter.

So I recommend using the Java Streams API. members returns a Java List Object. Stream (Java SE 11 & JDK 11 ).

var Transformation = Java.type("org.openhab.core.transform.actions.Transformation");
var openWindowNames = gWindowsfind
                        .members
                        .stream()
                        .filter(function(window) { return window.state == OPEN; })
                        .map(function(window) { return Transformation.transform("MAP", "fenster.map", window.name); })
                        .reduce(function(lst, name) { return lst + "-" + name + "\n"; })
                        .toString();

Thank you.

With the addition of itemRegistry.getItem to gWindowsfind it seems to work but the output with

logger.info('Offene Fenster: ’ +openWindowNames);

looks strange to me:

Offene Fenster: Optional[Schlafzimmer Velux Straßenseite-Arbeitsecke Velux Hofseite

I don’t know from where the “Optional[” is and why the “\n” does not create a line break.

Thank you again for your help!

OK, the reduce returns an Optional. That way there is something to return even if the reduce has nothing. I thought that toString() would give you the String but apparently it adds some stuff to it. You need to call get() on the result of the reduce to get at the String Object and then call toString().

Thanks,

.get()

does the job.