[SOLVED] Issues in a rule

Hello

I’ve found this message in my logs, but I don’t understand. Can you please help me?

2025-10-23 08:37:49.525 [WARN ] [el.core.internal.ModelRepositoryImpl] - DSL model 'presence.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.
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.

Here my rule:

// Global variables

var Timer cameHomeTimer = null

// Presence (Home) Unifi in

rule "Set Home"
when
    Member of gPhones changed to ON
then
    val setHome = gPhones.members.filter[ p | p.state == ON && p.name == triggeringItem.name].map[ name.replace("_Phone_Home_Unifi_online", "") ]

    switch setHome {
        case setHome.get(0) == "Axxxxx": {
            if (A_Presence.state == OFF) {
                A_Presence.sendCommand(ON)
            }
        }
        case setHome.get(0) == "Cxxx": {
            if (C_Presence.state == OFF) {
                C_Presence.sendCommand(ON)
            }
        }
        //case setHome.get(0) == "Cxxx": C_Presence.sendCommand(ON)
    }
end

// Presence (Home) welcome message

rule "Welcome home my dear"
when
    Member of gPresence changed to ON
then
    if(cameHomeTimer !== null) {
        return; // do nothing if there is already a Timer
    }
    
    logInfo("test", "Rule triggered by " + triggeringItem.name + " at " + now.toString)
    cameHomeTimer = createTimer(now.plusSeconds(5), [ |

        val cameHome = gPresence.members.filter[ h | h.state == ON && (h.name == triggeringItem.name || (h.lastUpdate !== null && h.lastUpdate.isAfter(now.minusSeconds(6)))) ].map[ name.replace("_Presence", "") ]

        gPresence.members.forEach[ h | logInfo("test", "Values for " + h.name + "\n" + "State = " + h.state.toString + "\n" + "LastUpdate = " + h.lastUpdate.toString)]

        var String message = "Benvenuti a casa: "

        switch(cameHome.size) {
            case 0: {
                logError("came home", "Unexpected number of phones: " + cameHome.size)
                return;
            }
            case cameHome.size == 1 && cameHome.get(0) == "Axxxxx": message = "Benvenuto a casa, Axxxxxx"
            case cameHome.size == 1 && cameHome.get(0) == "Cxxx": message = "Benvenuta a casa, Cxxx"
            case cameHome.size == 1 && cameHome.get(0) == "Sxxx": message = "Benvenuta a casa, Sxxxx"
            case cameHome.size == 1 && cameHome.get(0) == "Exx": message = "Benvenuto a casa, Exxxxx"
            case cameHome.size == 1 && cameHome.get(0) == "Axx": message = "Welcome home, Axx"
            case cameHome.size == 1 && cameHome.get(0) == "Cxxxx": message = "Welcome home, Cxxxx"
            default: {
                message = message + cameHome.reduce[ String list, String name | list + ", " + name ]
                val ind = message.lastIndexOf(",").toString
                //message = new StringBuilder(str).replace(ind, ind+1," e").toString();
                message = message.replace(ind, " e ").toString();
            }
        }
        logInfo("check message", "message is: " + message)

        Echo1_TTS_Volume.sendCommand('90')
        //Echo_Living_Room_TTS.sendCommand('<speak><break time="2s"/>' + message + '</speak>')
        Echo1_TTS.sendCommand(message)      
        cameHomeTimer = null
    ])
end

// Presence (Away) GPS out

rule "Set Away"
when
    Member of gPGPS changed to OFF
then
    val setAway = gPGPS.members.filter[ a | a.state == OFF && a.name == triggeringItem.name].map[ name.replace("_Presence_GPS", "") ]

    switch setAway {
        case setAway.get(0) == "Axxxx": A_Presence.sendCommand(OFF)
        case setAway.get(0) == "Cxxx": C_Presence.sendCommand(OFF)
    }
end

thanks for helping me

Andrea

As there are three rules in the file, the first step would be to separate them (temporarily) into three files.
What do the groups look like? I don’t get the point what the code is supposed for, I mean, e.g. the first rule:

rule "Set Home"
when
    Member of gPhones changed to ON
then
    val setHome = gPhones.members.filter[ p | 
		p.state == ON && 
		p.name == triggeringItem.name
	].map[ name.replace("_Phone_Home_Unifi_online", "") ]

    switch setHome {
        case setHome.get(0) == "Axxxxx": {
            if (A_Presence.state == OFF) {
                A_Presence.sendCommand(ON)
            }
        }
        case setHome.get(0) == "Cxxx": {
            if (C_Presence.state == OFF) {
                C_Presence.sendCommand(ON)
            }
        }
        //case setHome.get(0) == "Cxxx": C_Presence.sendCommand(ON)
    }
end

why don’t do it this way?

rule "Set Home"
when
    Member of gPhones changed to ON
then
    val String setHome = triggeringItem.name.replace("_Phone_Home_Unifi_online", "")
    switch(setHome) {
        case "Axxxxx": if(A_Presence.state == OFF) A_Presence.sendCommand(ON)
        case "Cxxx"  : if(C_Presence.state == OFF) C_Presence.sendCommand(ON)
    }
end
1 Like

A closure is sometimes called a lambda and sometimes called a function. In Rules DSL any time you have [ | ] you are defining a closure.

That should help narrow down which lines of code the error is coming from.

The arguments passed to a closure are the stuff before the | which further narrows the problem down.

The specific problem, reworded in more generic terms is “I can’t figure out what these arguments are, consider telling me.” You actually do this for one of the closures.

message = message + cameHome.reduce[ String list, String name | list + ", " + name ]

There you are telling the language parser that list is a String and name is a String.

So you just need to do that for the other lambdas in the rule where it’s complaining.

val setHome = gPhones.members.filter[ GenericItem p | p.state == ON...

Though, as @Udo_Hartmann points out, you can likely eliminate a bunch of these closures entirely.

2 Likes

Thank you so much @Udo_Hartmann @rlkoshak for your example and explanation.

I’ve understood, and now I’ve fixed/optimized the rule.

thanks again

Andrea