New error in old Rule after updating OH

Hi there. One of my Rules has stopped working with the error

DSL model 'plane.rules' has errors, therefore ignoring it: There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

I think I’ve been able to narrow down the erroring bit of code, but unfortunately the error seems to be occurring in a function that was kindly written for me by Rich Koshak, one of the smart people on here. The Rule has worked flawlessly since he wrote it in April 2024 and has stopped working in the last week or so. I don’t understand the error and I must ask for help. Here’s the few lines of text:

       // Lets use a little lambda to make the next section a little simpler
       // Test the value against the compareTo, if they are the same it's bad
       // so log it out with the passed in message. Return false if valid is false or 
       // isBad is true.
       // Usage: call with the previous result so as soon as one value is bad we mark
       // the whole Packet as bad.


        var isValid = [ value, compareTo, valid, msg | 
            var isBad = (value == compareTo)
             if (isBad) {
//                logWarn(LOGNAME, msg)
             }
             valid && !isBad
        ]

        TxValid = isValid.apply(PlaneICAO_TXT, Packet_TXT, TxValid, "ICAO fail")
        TxValid = isValid.apply(PlaneCallsign_TXT, Packet_TXT, TxValid, "callsign fail")
        TxValid = isValid.apply(PlaneContactAge_TXT, Packet_TXT, TxValid, "age fail")
        TxValid = isValid.apply(PlaneAltitude_TXT, Packet_TXT, TxValid, "alt fail")
        TxValid = isValid.apply(PlaneVertRate_TXT, Packet_TXT, TxValid, "vert fail")
        TxValid = isValid.apply(PlaneLatitude_TXT, Packet_TXT, TxValid, "lat fail")
        TxValid = isValid.apply(PlaneLongitude_TXT, Packet_TXT, TxValid, "long fail")

        logWarn (LOGNAME, "validity {}", TxValid)

        // filter to pass only Packets containing beluga icao prefixes
       if ((TxValid) && ((ICAO_PREFIXES_TXT.contains(PlaneICAO_TXT.substring(0,3))))) {

etc.

I haven’t changed or even edited this code since April 2024 as it Just Works. Until now.

Let me know what other information I can supply.

I have temporarily rolled back my OH install to 5.0.0-1 and the Rule is once again working, so I guess it’s a behaviour change in OH?

This is a change in the upstream Xtend library. It’s more strict about typing in some places.

Where ever you have a closure (i.e. [ <variables> | <code> ]) you need to provide a type for the variables.

I don’t know the types of these variables for certain but I would guess

[ String value, String compareTo, boolean valid, String msg |

Thank you very much, Rich. Not only did you write that code snippet above, you debugged it 18 months later.

I simply did not know where to start.

A minor detail, which is irrelevant for the raised problem:

DSL Rules/Scripts/Transformations and Xtend all use XBase. Xtend is not used for running DSL Rules/Scripts/Transformations.

So the change must be in the upstream XBase.

This has been finally clarified by Clarification: Rules DSL derives from XBase and is similar to Xtend by dilyanpalauzov · Pull Request #2623 · openhab/openhab-docs · GitHub.

I have the same Issues in my rule.

rule "Benachrichtigung Temperatur"
when
    Member of Temperaturen_all changed
then

    // Check ob Variablen passen, ansonsten defaultwert setzen
    if ((NotificationTemperatur.state != OFF) && (NotificationTemperatur.state != ON)) {
        NotificationTemperatur.sendCommand(ON)
        logInfo('rules', logPrefix + 'Setze Default-Wert für NotificationTemperatur=An') 
    }
    if (fensterOffenWarnungTemperatur.state == NULL) {   
        fensterOffenWarnungTemperatur.postUpdate(17)
        logInfo('rules', logPrefix + 'Setze Default-Wert für fensterOffenWarnungTemperatur=17')
    }
    
    val Number minTemp = fensterOffenWarnungTemperatur.state as Number

    if (NotificationTemperatur.state == ON)
    {
      if(((Temperaturen_all.members.filter[i|(i.state as Number) <= minTemp]).size > 0) && (nNotifications == 0))
      {    
        nNotifications = 1

        if(now.getHour() >= 22 || now.getHour() < 7)                               // keine Nachricht zwischen 22 Uhr und 07 Uhr versenden
            logInfo("Raumtemperatur","Nachricht nicht gesendet, da es Nacht ist")
        else if((gKontakte.state as Number) == 0)
            logInfo("Raumtemperatur","Nachricht nicht gesendet, da kein Fenster mehr offen ist")
        else {
            Temperaturen_all.members.filter[i|(i.state as Number) <= minTemp].forEach[j|
            var String tempitem =  j.name 
            val Number tempvalue = j.state as Number
            meldeText = 'Die Temperatur im Raum ' + tempitem.replaceAll('KG_','Kellergeschoß_').replaceAll('EG_','Erdgeschoß ').replaceAll('OG_','Obergeschoß ').replaceAll('_Temp',' ').replaceAll('Kind2_XTemp','Theo ').replaceAll('SpeisXTemp','Speis ') + 'ist kleiner als ' + minTemp.toString + ' °C! Aktuell: ' + tempvalue.toString + ' °C'
            val actions = getActions("pushover", "pushover:pushover-account:***")         //Pushover
            actions.sendMessage("Information", meldeText)
    ]
        }

        tFenstertemp = createTimer(now.plusMinutes(15))[  // Timer damit die Meldung nur alle 15 Minuten kommt und nicht ständig
        nNotifications = 0
        ]   
      }
    }

end

When I try to add the type “NumberItem”

 if(((Temperaturen_all.members.filter[NumberItem i|(i.state as Number) <= minTemp]).size > 0) && (nNotifications == 0))

i get the Error:

Type mismatch: cannot convert from (NumberItem)=>boolean to Function1<? super Item, Boolean>

What am I doing wrong?

I cannot tell you why this happens, but you can try to skip the single parameter all together, then it is is assumed that the parameter is called it ⇐ this might help, as you cannot type the parameter type in a wrong way. (This is described at https://eclipse.dev/Xtext/documentation/305_xbase.html#xbase-expressions-lambda).

Irrespective of your problem, you can use exists instead of filter, ref: xtext/org.eclipse.xtext.xbase.lib/src/org/eclipse/xtext/xbase/lib/IterableExtensions.java at main · eclipse-xtext/xtext · GitHub :

if (Temperaturen_all.members.exists[(it.state as Number) <= minTemp]
    && nNotifications == 0)

If it does not help, you can try to convert boolean to Boolean:

Temperaturen_all.members.exists[ Boolean.valueOf((it.state as Number) <= minTemp) ]

This is how it works:

Temperaturen_all.members invokes GroupItems.getMembers(), which returs Set<Item>.

Set<Item> invokes xtext/org.eclipse.xtext.xbase.lib/src/org/eclipse/xtext/xbase/lib/IterableExtensions.java at main · eclipse-xtext/xtext · GitHub :

public static <T> Iterable<T> filter(Iterable<T> unfiltered, Function1<? super T, Boolean> predicate) {
  return Iterables.filter(unfiltered, new BooleanFunctionDelegate<T>(predicate));
}

thus IteratorExtensions.filter(Set<Item>, Function1<? super Item, Boolean> predicate). ? super Item means that the parameter can be substituted with anything Item is based on, e.g. Identifiable<String> or Object, but not classes, which derive from Item. For this reason NumberItem cannot be used as parameter, but [Item i|i.state as Number <= minTemp] might work.

The real problem is that for some reason between openHAB 5.0 and 5.1, the implicit detection of the types stopped working, so now many parameters must be spelled.

Sorry, but I’m afraid I didn’t quite understand that.

This code works fine.

Temperaturen_all.members.exists[ Boolean.valueOf((it.state as Number) <= minTemp) ]

But there’s still a problem with that.

Temperaturen_all.members.filter[i|(i.state as Number) <= minTemp].forEach[j|

If I try this

Temperaturen_all.members.exists[ Boolean.valueOf((it.state as Number) <= minTemp) ].forEach[j| 

I get the Error

DSL model ‘notification2.rules’ has errors, therefore ignoring it: There is no context to infer the closure’s argument types from. Consider typing the arguments or put the closures into a typed context.

That doesn’t work either

Temperaturen_all.members.filter[Item i|(i.state as Number) <= minTemp].forEach[j|

Error

DSL model ‘notification2.rules’ has errors, therefore ignoring it: [110,45]: no viable alternative at input ‘Item’ [110,50]: no viable alternative at input ‘i’ [110,82]: mismatched input ‘]’ expecting ‘}’ [125,5]: extraneous input ‘}’ expecting ‘end’

As I explained in another thread, we changed something in 5.1 leading to some validation checks being now considered as errors while they were before just warmings.

Maybe we have to make an exception for DSL rules.

I agree [Item i | is not working, but when I try to reproduce your case I do not get these errors.

https://community.openhab.org/t/text-rules-are-randomly-not-loaded-in-openhab-5-1/ suggests using [GenericItem i | instead of [ i |.

Independent of the other information, I think the brackets in (i.state as Number) <= minTemp are not necessery. You can use now.hour instead of now.getHour().

For

if(((Temperaturen_all.members.filter[NumberItem i|(i.state as Number) <= minTemp]).size > 0) && (nNotifications == 0))

the error is

Type mismatch: cannot convert from (NumberItem)=>boolean to Function1<? super Item, Boolean>

because you had this snippet

[i|(i.state as Number) <= minTemp]

on two places, I proposed to you how to replace it, and you replaced it only on one of the places.

For

Temperaturen_all.members.exists[ Boolean.valueOf((it.state as Number) <= minTemp) ].forEach[j| 

the error

DSL model ‘notification2.rules’ has errors, therefore ignoring it: There is no context to infer the closure’s argument types from. Consider typing the arguments or put the closures into a typed context.

is not because of exitst[…], but because of forEach[j |. Also I suggested replacing .filter[…].size > 0 with exits[…]. But you have tried in Temperaturen_all.members.filter[i|(i.state as Number) <= minTemp].forEach[j| to replace also filter[…] with exists[…], which changes the logic.

All that said, in your original snippet replace [ j | ` with `[GenericItem j | ` and replace twice `[ i |` with `[GeneraicItem i |`. That’s all.

As an alternative replace [i | and [j | with just [, and in the lambda body instead of i or j use it (it is highlighted : use the letter i followed by letter t, as if [it | were present):

    if(Temperaturen_all.members.exists[it.state as Number <= minTemp] && nNotifications == 0)
    {    
        nNotifications = 1

        if(now.hour >= 22 || now.hour < 7)                               // keine Nachricht zwischen 22 Uhr und 07 Uhr versenden
            logInfo("Raumtemperatur","Nachricht nicht gesendet, da es Nacht ist")
        else if(Kontakte.state as Number == 0)
            logInfo("Raumtemperatur","Nachricht nicht gesendet, da kein Fenster mehr offen ist")
        else
            Temperaturen_all.members.forEach[
              val tempvalue = it.state as Number
              if (tempValue <= minTemp) return;
              meldeText = 'Die Temperatur im Raum ' + it.name.replaceAll('KG_','Kellergeschoß_').replaceAll('EG_','Erdgeschoß ').replaceAll('OG_','Obergeschoß ').replaceAll('_Temp',' ').replaceAll('Kind2_XTemp','Theo ').replaceAll('SpeisXTemp','Speis ') + 'ist kleiner als ' + minTemp.toString + ' °C! Aktuell: ' + tempvalue.toString + ' °C'
              val actions = getActions("pushover", "pushover:pushover-account:***")         //Pushover
              actions.sendMessage("Information", meldeText)
            ]
        tFenstertemp = createTimer(now.plusMinutes(15)) [ nNotifications = 0 ]
    }

I cited wrong. Correct is GenericItem, incorrect is Genericitem.

I forgot initially to put a semi-colon after return;. I updated the snippet above.

The snippet work, but I have to add the barket in it.state as Number <= minTemp

Thank you!