Transferred rule from OH2 not working in OH3 due to non-final variable in lambda

Hi,

With OH2 I used the following rule to create a report of open windows/doors when sending a notification after a door or window has been opened:

 var report = ""
 G_doors.allMembers.filter([state == OPEN]).forEach([ContactItem item | report = report + "\n" + item.label ])
 val message = "Diese Türen sind geöffnet:\n" + report
 mailActions.sendMail("...@...", "Tür " + triggeringItem.label + " wurde geöffnet", message)

These rules work just fine in the OH2 instance I’m still running in parallel.
After adding these rules to OH3 I see the following error in the logs each time it is triggered:

15:30:03.533 [ERROR] [.internal.handler.ScriptActionHandler] - Script execution of rule with UID '9f8e1f2e01' failed: logInfo("Doors", "Tür " + triggeringItem.label + " wurde geöffnet.")
val mailActions = getActions("mail","mail:smtp:xxx") val telegramActions = getActions("telegram","telegram:telegramBot:xxxxx")
var report = "" G_doors.allMembers.filter([state == OPEN]).forEach([ContactItem item | report = report + "\n" + item.label ]) val message = "Diese Türen sind geöffnet:\n" + report mailActions.sendMail("...@...", "Tür " + triggeringItem.label + " wurde geöffnet", message) telegr ___ amActions ___ .sendTelegram("Tür " + triggeringItem.label + " wurde geöffnet")
   1. Cannot refer to the non-final variable report inside a lambda expression; line 3, column 286, length 6
   2. Cannot refer to the non-final variable report inside a lambda expression; line 3, column 295, length 6

I’m unsure how I would be able to concatenate the list of open doors/windows, when the variable must be final.
Does anyone have a suggestion on how I could change the rule to work with OH3?

Thanks a lot for your help!

There are a number of approaches. You can use a StringBuider instead of a plain old string. Or you can use a map/reduce.

I’m surprised this worked for you before because as long as I can remember you’ve never been able to assign to a non-final variable in a forEach lambda.

Search for the Working with Grips in Rules design pattern for some examples of map/reduce.

In Jython and JavaScript there are additional approaches as well.

Thanks a lot for pointing me in the right direction.
And by the way: What a great effort to provide all these tutorials and examples!
I can’t stop reading to improve my rules :slight_smile:

I was able to get it working like this:

    StringBuilder message = new StringBuilder
    message.append(triggeringItem.label + " wurde geöffnet\n")
    message.append("Folgende Fenster sind geöffnet:\n")
    G_windows.members.filter[ window | window.state == OPEN ].forEach[window | message.append(window.label + "\n") ]

I want to filter the triggering item from the list of open windows but have not yet succeeded.
I tried:

    var openWindows = G_windows.members.filter[ window | window.state == OPEN ]
    //openWindows.remove(triggeringItem)
    if (openWindows.size > 0) {
      message.append("Folgende Fenster sind offen:\n")
      openWindows.forEach[window | message.append(window.label + "\n") ]
    }

The open windows don’t get appended anymore and I haven’t even tried the .remove() :frowning:
Do you have an idea what could be wrong?
Thanks a lot for your help!

You can include a check in the filter to exclude the triggering Item

var openWindows = G_windows.members.filter[ window | window.name != triggeringItem.name && window.state == OPEN]

Are you sure you have window Items that are in the OPEN state?

I don’t see anything obviously wrong with the code posted. But make sure that you know the states of all the members of G_windows when you run the rule to understand what it’s doing.

Hi Rich,
thanks for the hint!
It seems that the comments I added with // did break the script.
After removing all lines with // it worked fine…

By the way, do you know how I can prevent the code style from being “cleaned up” each time I open a script?
It seems that OH always tries to shorten the script as much as possible and I spent a minute each time I revisit a script to rearrange the line breaks…

Do you mean in MainUI? I’ve not noticed it doing anything like that. The YAML code representation insists inserts two newlines for every one but that is more of a work around for how it gets saved in JSONDB I think. The editor for the script action though she’s it as you wrote it. If you are using some other editor you’ll have to look into that editor’s settings.

Hi Rich,
I was referring to the code editor in the new UI.
I created one of my rules with this code style:

After saving and reopening the editor later, it looks like this:

The outcome doesn’t even seem to be deterministic, as I had the whole if-block move to one line like this once:

It doesn’t hurt too much, once the rules are done, but it still seems strange.

Just another thing I couldn’t yet find an answer to.
Do you know the difference between these?
script:
script: >
script: >-

Thanks a lot!

Yes, that is what I was referring to with the YAML view. The YAML is basically a direct transformation of what is stored in the JSONDB as JSON. There are some issues with newlines and JSON which need to be worked around.

The only way I’ve found to keep the code consistently on new lines is to have an extra new line for every new line. But I edit the code in the code editor instead of the YAML view.

For example, here is the YAML view of one of my simpler rules:

triggers:
  - id: "1"
    configuration:
      groupName: ServiceStatuses
      state: ON
    type: core.GroupStateChangeTrigger
  - id: "2"
    configuration:
      groupName: ServiceStatuses
      state: OFF
    type: core.GroupStateChangeTrigger
conditions:
  - inputs: {}
    id: "4"
    configuration:
      type: application/javascript
      script: event.oldItemState.class != UnDefType.class
    type: script.ScriptCondition
actions:
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript
      script: >
        var logger =
        Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.OfflineAlert");

        scriptExtension.importPreset("default");

        this.OPENHAB_CONF = (this.OPENHAB_CONF === undefined) ? java.lang.System.getenv("OPENHAB_CONF") : this.OPENHAB_CONF;

        load(OPENHAB_CONF + "/automation/lib/javascript/personal/utils.js")


        var status = (event.itemState == ON) ? "online" : "offline";

        this.sendAlert(this.getName(event.itemName) + " is now " + status + "!", logger);
    type: script.ScriptAction

And here is what the Script Action looks like:

var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.OfflineAlert");
scriptExtension.importPreset("default");
this.OPENHAB_CONF = (this.OPENHAB_CONF === undefined) ? java.lang.System.getenv("OPENHAB_CONF") : this.OPENHAB_CONF;
load(OPENHAB_CONF + "/automation/lib/javascript/personal/utils.js")

var status = (event.itemState == ON) ? "online" : "offline";
this.sendAlert(this.getName(event.itemName) + " is now " + status + "!", logger);

I only edited the code from the Design tab. The extra new lines were put into the code shown in YAML automatically. And when I remove the extra newlines from the YAML view, the code ends up all on one line in the Design tab.

Hi Rich,

thanks for the suggestion!
I’ll avoid the YAML view for now :wink:
That seems to work well.

Do you know the difference between these?
script:
script: >
script: >-
I haven’t found a documentation for it yet :frowning:
It seems, you’re using the : > variant…

I didn’t add it. It’s another one of those things that happens when the JSON is converted to YAML for display in the browser. I suspect it’s a way to indicate that the next set of stuff is to be treated as one unit instead of trying to interpret each individual line. I’ve seen | used for that in Ansible scripts. But that’s just an assumption and not based on any real knowledge.

Sounds plausible enough.
Strange though, that all of it seems to work without recognizable difference…
Anyway.
Thanks a lot for your help and insights!

It’s one of the joys of YAML. :wink: It’s super picky about some things and pretty flexible about others.