[SOLVED] Roller shutter with lambda function

Hi,

i´m trying to close and open my roller shutter with a lambda function. Therefore i have.

The rule:

rule "Abschattung starten"
when
    Item Azimuth changed
then
    val String logPrefix = "Abschattung aktivieren - "
    
    if (AutoShading_Azimuth_east == "NULL" ||  AutoShading_Azimuth_south == "NULL" || AutoShading_Azimuth_west == "NULL" ){
            AutoShading_Azimuth_east.postUpdate(closingAzimuth_west)
            AutoShading_Azimuth_south.postUpdate(closingAzimuth_south)
            AutoShading_Azimuth_west.postUpdate(closingAzimuth_west)
    }

    if (AutoShading.state == ON && IsDay.state == ON) {
            //Azimuth ist größer als 181° (WEST)
            if (returnvalueWest == false) { 
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_west)) {
                    if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden WEST")
                    returnvalueWest = shadingStart.apply(gRollladenWest) as boolean
                    return;
                }      
            } 
           
            //Azimuth ist größer als 111° (SÜD)
           if (returnvalueSouth == false) { 
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_south)) {
                    if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden SÜD")
                    returnvalueSouth = shadingStart.apply(gRollladenSouth) as boolean
                    return;
                }
           }

            //Azimuth ist größer als 65° (OST)
            if (returnvalueEast == false) { 
                if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden OST" + returnvalueEast.toString())
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_east)) {
                if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden OST")
                returnvalueEast = shadingStart.apply(gRollladenEast) as boolean
                return
                }
            }
    
    } else {
            if (log) logInfo(filename, logPrefix + "Abschattung ist deaktiviert")
           }

end

The lambda:

val Functions$Function1<GroupItem, boolean> shadingStart= [ shutter |

                logInfo (filename, logPrefix + "FUNKTION AUSGEFÜHRT")
                if ((localCurrentTemperature.state as Number).floatValue <= (AutoShading_Temp_min.state as Number)) {
                        if (log) logInfo (filename, logPrefix + "Mindesttemperatur von (" + AutoShading_Temp_min.state.toString() + ") wurde nicht erreicht. Aktuelle Temperatur (" + localCurrentTemperature.state.toString() + ")")
                        return false;
                }

                if ((localCurrentCloudiness.state as Number).floatValue > (AutoShading_Cloudiness_max.state as Number)) { 
                        if (log) logInfo (filename, logPrefix + "Aktuelle Bewoelkung (" + localCurrentCloudiness.state.toString() + ") ueberschreitet den eingestellten Grenzwert von (" + AutoShading_Cloudiness_max.state.toString() + ")")
                        return false;
                }

                if ((Elevation.state as Number) <= (AutoShading_Elevation_end.state as Number)) {
                        if (log) logInfo (filename, logPrefix + "Elevation für das Beenden der Abschattung (" + AutoShading_Elevation_end.state.toString() + ") ist groesser als aktuelle (" + Elevation.state.toString() + ")")
                        return false;
                }

                // Rollladen werden geschlossen
                if (log) logInfo(filename, logPrefix + "Rollladen werden geschlossen")
                
                shutter?.members.forEach[ blind |
                    if ((blind.state as Number) < (blind_closed)) {
                        if (log) logInfo(filename, logPrefix + "Fahre Rollladen auf " + blind_closed.toString() + "%: " + blind.name)
                        blind.sendCommand(blind_closed)
                        //Pushnachricht versenden
                        //sendPushoverMessage(pushoverBuilder("Abschattung für " + blind.name.toString "aktiviert"))
                    } else {
                        if (log) logInfo (filename, logPrefix + "Rollladen ist bereits weiter geschlossen (" + blind.state.toString() + "%) als er geschlossen werden sollte und wird daher ignoriert")
                    }
                ]
                return true;
]

but the function seems not to be called. No errors in the logs. Last entry in the log is:
shutter.rules Abschattung aktivieren - Abschatung für Rollläden WEST

Thx

I do not see anything wrong at first glance.
Eventually try typecasting the argument when calling the lambda to see if that makes a difference.
Did you enable parser logs? Possibly there is an error with the lambda and you don’t know ?

Markus comments prompted a thought - that first logInfo uses variables filename, logPrefix. Are those globals defined at file level? Unfortunately, so is the lambda, and globals cannot “see” each other.
I don’t know where your error message went, I’d expect a null error report.

You have to pass everything except Items into a lambda.
Of course in this case, just use string constants.
logInfo(“myLambda” , “starting”)

Hi Guys,

ok, I will try your suggestions and give you an update on that.

best regards

not sure how…like this log:set DEBUG org.openhab… but which one?

I see a few things worth comment, though none of these should be a problem.

  • Use the newer (since OH 2.3) syntax. It’s cleaner and easier to figure out what it does. You also don’t need any special imports.
val shadingStart = [GroupItem shutter |
    ...
]
  • As rossko57 identified, you must pass 1filenameandlogPrefix` (and any other variable whether it’s a global or not) to the lambda if the lambda is declared as a global.

  • I would actually recommend creating the lambda as a variable inside your Rule. I’m assuming that this lambda is only called from this one Rule. If you declare it inside the Rule then the lambda can inherit and be aware of any variables that are declared before it so logPrefix won’t need to be passed to it. It will also be less prone to certain types of run time errors.

  • If you apply some of the techniques discussed in Design Pattern: DRY, How Not to Repeat Yourself in Rules DSL, particularly Design Pattern: How to Structure a Rule you ought to be able to eliminate the need for the lambda in the first place.

  • Add logging before and after the call to the lambda. Add a simple log statement as the first line of the lambda.

  • Consider putting the body of your lambda inside a try/catch and log any errors, just in case you are losing any errors generated inside the lambda. This won’t catch all errors/exceptions (e.g. type exceptions are caught outside your Rule) but it can help.

1 Like

Hi Rich,

ok, now it looks like this:

val String filename = "shutter.rules"
val String logPrefix = "Abschattung aktivieren - "

var boolean log = true
//var boolean returnvalue = false
var boolean returnvalueWest = false
var boolean returnvalueEast = false
var boolean returnvalueSouth = false

val Number blind_closed = 100
val Number blind_open = 0
val Number blind_translucent = 35

var Number closingAzimuth_west = 181
var Number closingAzimuth_south = 111
var Number closingAzimuth_east = 65

var Number closingCloudiness = 25

val shadingStart = [GroupItem shutter |
    
                logInfo ("log", "FUNKTION AUSGEFÜHRT")

                if ((localCurrentTemperature.state as Number).floatValue <= (AutoShading_Temp_min.state as Number)) {
                        if (log) logInfo (filename, logPrefix + "Mindesttemperatur von (" + AutoShading_Temp_min.state.toString() + ") wurde nicht erreicht. Aktuelle Temperatur (" + localCurrentTemperature.state.toString() + ")")
                        return false;
                }

                if ((localCurrentCloudiness.state as Number).floatValue > (AutoShading_Cloudiness_max.state as Number)) { 
                        if (log) logInfo (filename, logPrefix + "Aktuelle Bewoelkung (" + localCurrentCloudiness.state.toString() + ") ueberschreitet den eingestellten Grenzwert von (" + AutoShading_Cloudiness_max.state.toString() + ")")
                        return false;
                }

                if ((Elevation.state as Number) <= (AutoShading_Elevation_end.state as Number)) {
                        if (log) logInfo (filename, logPrefix + "Elevation für das Beenden der Abschattung (" + AutoShading_Elevation_end.state.toString() + ") ist groesser als aktuelle (" + Elevation.state.toString() + ")")
                        return false;
                }

                // Rollladen werden geschlossen
                if (log) logInfo(filename, logPrefix + "Rollladen werden geschlossen")
                
                shutter?.members.forEach[ blind |
                    if ((blind.state as Number) < (blind_closed)) {
                        if (log) logInfo(filename, logPrefix + "Fahre Rollladen auf " + blind_closed.toString() + "%: " + blind.name)
                        blind.sendCommand(blind_closed)
                        //Pushnachricht versenden
                        //sendPushoverMessage(pushoverBuilder("Abschattung für " + blind.name.toString "aktiviert"))
                    } else {
                        if (log) logInfo (filename, logPrefix + "Rollladen ist bereits weiter geschlossen (" + blind.state.toString() + "%) als er geschlossen werden sollte und wird daher ignoriert")
                    }
                ]
                return true;
]

and the rule:

rule "Abschattung starten"
    when
        Time cron  "0/30 * * * * ?"
    then
        val String logPrefix = "Abschattung aktivieren - "
        
        if (AutoShading_Azimuth_east == "NULL" ||  AutoShading_Azimuth_south == "NULL" || AutoShading_Azimuth_west == "NULL" ){
            AutoShading_Azimuth_east.postUpdate(closingAzimuth_west)
            AutoShading_Azimuth_south.postUpdate(closingAzimuth_south)
            AutoShading_Azimuth_west.postUpdate(closingAzimuth_west)
    }

    if (AutoShading.state == ON && IsDay.state == ON) {
            //Azimuth ist größer als 181° (WEST)
            if (returnvalueWest == false) { 
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_west)) {
                    if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden WEST " + closingAzimuth_west)
                    //if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden WEST")
                    returnvalueWest = shadingStart.apply(gRollladenWest)
                    if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden WEST " + gRollladenWest)
                    if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden WEST " + returnvalueWest)
                    return;
                }      
            } 
           
            //Azimuth ist größer als 111° (SÜD)
           if (returnvalueSouth == false) { 
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_south)) {
                    if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden SÜD")
                    returnvalueSouth = shadingStart.apply(gRollladenSouth)
                    return;
                }
           }

            //Azimuth ist größer als 65° (OST)
            if (returnvalueEast == false) { 
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_east)) {
                if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden OST")
                returnvalueEast = shadingStart.apply(gRollladenEast)
                return;
                }
            }
    
    } else {
            if (log) logInfo(filename, logPrefix + "Abschattung ist deaktiviert")
           }

end

now the log tell me this:

2019-07-10 20:26:10.807 [INFO ] [smarthome.model.script.shutter.rules] - Abschattung aktivieren - Abschatung für Rollläden WEST 181

2019-07-10 20:26:10.811 [INFO ] [g.eclipse.smarthome.model.script.log] - FUNKTION AUSGEFÜHRT

2019-07-10 20:26:10.821 [INFO ] [smarthome.model.script.shutter.rules] - Abschattung aktivieren - Abschatung für Rollläden WEST gRollladenWest (Type=GroupItem, BaseType=RollershutterItem, Members=2, State=NULL, Label=Rollläden (West), Category=rollershutter, Groups=[gRollladen])

2019-07-10 20:26:10.825 [INFO ] [smarthome.model.script.shutter.rules] - Abschattung aktivieren - Abschatung für Rollläden WEST false

so the lambda is called, see first line in the lambda “FUNKTION AUSGEFÜHRT”…but thats all…not sure why.

if (log) logInfo (filename, logPrefix + "Mindesttemperatur v

You didn’t pass filename into the lambda. You didn’t pass logPrefix into the lambda.

As both rossko57 and I have said

you must pass filename and logPrefix (and any other variable whether it’s a global or not) to the lambda if the lambda is declared as a global.

If it isn’t declared inside the lambda, it has to be passed to it, or it doesn’t exist.

ahhh, you‘re right. i was focused on the VAL. I‘ll try again and get back to you.

Hi Rich, Hi rossko,

Thx for your support and the hints. I want to share the code…maybe it’s helping someone out.

import java.util.Map

// Das Logging kann für diese Rules separat aktiviert werden (log=true)
var boolean log = true
var boolean message = true

val String filename = "shutter.rules"

var boolean returnvalueWest = false
var boolean returnvalueEast = false
var boolean returnvalueSouth = false

val Number blindClosed = 100
val Number blindOpen = 0
val Number blindTranslucent = 35

var Number closingAzimuth_west = 181
var Number closingAzimuth_south = 111
var Number closingAzimuth_east = 65

var Number closingCloudiness = 25
var Number openingTemperature = 22

val Map<String, Number> targetHights = newHashMap ( 
    "KuecheRollladenTuer" -> 58,
    "SchlafzimmerRollladenTuer" -> 100,
    "BadezimmerRollladen" -> 55,
    "WohnzimmerRollladenEsstisch" -> 65,
    "WohnzimmerRollladenTuer" -> 68,
    "WohnzimmerRollladenBalkon" -> 100
)

/* -------------  Automatische Abschattung ein/aus --------------------
*/

//-------------  Abschattung FUNKTION --------------------

val shadingStart = [GroupItem shutter |

        val String logPrefix = "Abschattung aktivieren - "
        var boolean log = true

        logInfo ("Lambda", logPrefix + shutter)

                val Map<String, Number> targetHights = newHashMap ( 
                "KuecheRollladenTuer" -> 58,
                "SchlafzimmerRollladenTuer" -> 100,
                "BadezimmerRollladen" -> 55,
                "WohnzimmerRollladenEsstisch" -> 65,
                "WohnzimmerRollladenTuer" -> 68,
                "WohnzimmerRollladenBalkon" -> 100
                )

                if ((localCurrentTemperature.state as Number).floatValue <= (AutoShading_Temp_min.state as Number)) {
                        if (log) logInfo ("Lambda", logPrefix + "Mindesttemperatur von (" + AutoShading_Temp_min.state.toString() + ") wurde nicht erreicht. Aktuelle Temperatur (" + localCurrentTemperature.state.toString() + ")")
                        return false;
                }

                if ((localCurrentCloudiness.state as Number).floatValue > (AutoShading_Cloudiness_max.state as Number)) { 
                        if (log) logInfo ("Lambda", logPrefix + "Aktuelle Bewoelkung (" + localCurrentCloudiness.state.toString() + ") ueberschreitet den eingestellten Grenzwert von (" + AutoShading_Cloudiness_max.state.toString() + ")")
                        return false;
                }

                if ((Elevation.state as Number) <= (AutoShading_Elevation_end.state as Number)) {
                        if (log) logInfo ("Lambda", logPrefix + "Elevation für das Beenden der Abschattung (" + AutoShading_Elevation_end.state.toString() + ") ist groesser als aktuelle (" + Elevation.state.toString() + ")")
                        return false;
                }

                // Rollladen werden geschlossen
                if (log) logInfo("Lambda", logPrefix + "Grenzwert wurden erreicht, Rollladen werden geschlossen")

                shutter.members.forEach[ blind |
                    if ((blind.state as Number) < targetHights.get(blind.name.toString()).intValue) {
                        logInfo ("Lambda", logPrefix + "Fahre Rollladen (" + blind.name.toString() + ") auf (" + targetHights.get(blind.name.toString()).intValue + ") %" )
                        //blind.sendCommand(targetHights.get(blind.name.toString()).intValue)
                        //Pushnachricht versenden
                        sendPushoverMessage(pushoverBuilder("Abschattung für " + blind.name.toString + " aktiviert, schließen auf " + targetHights.get(blind.name.toString()).toString() + " %"))
                    } else {
                        logInfo ("Lambda", logPrefix + "Rollladen ist bereits weiter geschlossen (" + blind.state.toString() + "%) als er geschlossen werden sollte und wird daher ignoriert")
                    }
                ]
                AutoShading_Start_last.postUpdate(now.toString())
    return true;
]

//-------------  Abschattung starten --------------------

rule "Abschattung starten"
when
    Time cron  "0/30 * * * * ?" or
    Item Azimuth changed
then
    val String logPrefix = "Abschattung aktivieren - "
    
    //Prüfung ob Abschattungsautomatik an ist und ob es Tag ist
    if (AutoShading.state == ON && IsDay.state == ON) {
    
            //var String timeNow = now.toString().substring(0,10)
            //var String timeLastStart = AutoShading_Start_last.state.toString().substring(0,10)
            
            // Items für Sitemap füllen
            if (AutoShading_Azimuth_east == "NULL" ||  AutoShading_Azimuth_south == "NULL" || AutoShading_Azimuth_west == "NULL" ){
                    AutoShading_Azimuth_east.postUpdate(closingAzimuth_west)
                    AutoShading_Azimuth_south.postUpdate(closingAzimuth_south)
                    AutoShading_Azimuth_west.postUpdate(closingAzimuth_west)
            }
            
            //Azimuth ist größer als 181° (WEST)
            if (returnvalueWest == false) { 
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_west)) {
                    if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden WEST")
                    returnvalueWest = shadingStart.apply(gRollladenWest)
                    return;
                }      
            } 
           
            //Azimuth ist größer als 111° (SÜD)
           if (returnvalueSouth == false) { 
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_south) && (Azimuth.state as Number).floatValue < (closingAzimuth_west)) {
                    if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden SÜD")
                    returnvalueSouth = shadingStart.apply(gRollladenSouth)
                    return;
                }
           }

            //Azimuth ist größer als 65° (OST)
            if (returnvalueEast == false) { 
                if ((Azimuth.state as Number).floatValue > (closingAzimuth_east) && (Azimuth.state as Number).floatValue < (closingAzimuth_south)) {
                if (log) logInfo (filename, logPrefix + "Abschatung für Rollläden OST")
                returnvalueEast = shadingStart.apply(gRollladenEast)
                return;
                }
            }
    
    } else {
            if (log) logInfo(filename, logPrefix + "Abschattung ist deaktiviert, oder es ist Nacht.")
           }

end

//-------------  Abschattung beenden --------------------

rule "Abschattung beenden"
when 
    Time cron  "0/45 * * * * ?" or
    Item Elevation changed
then   
    if (AutoShading_end.state == ON && IsDay.state == ON) {
                    
            val String logPrefix = "Abschattung deaktivieren - "
            
            var String timeNow = now.toString().substring(0,10)
            var String timeLastEnde = AutoShading_End_last.state.toString().substring(0,10)


            if (timeLastEnde == timeNow) {
                if (log) logInfo(filename, logPrefix + "Beende, da heute bereits ein automatisches Hochfahren stattfand")
                return;
            }

            if ((localCurrentTemperature.state as Number).floatValue > (AutoShading_Temp_raise.state as Number)) {
                if (log) logInfo(filename, logPrefix + "Beende, aktuelle Temperatur (" + localCurrentTemperature.state.toString() + ") ueberschreitet den eingestellten Grenzwert zum hochfahren von (" + AutoShading_Temp_raise.state.toString()+ ") ")
                return;
            }

            if ((Elevation.state as Number) > (AutoShading_Elevation_end.state as Number)) {
               if (log) logInfo(filename, logPrefix + "Beende, aktuelle Elevation (" + Elevation.state.toString() + ") ueberschreitet den eingestellten Grenzwert von (" + AutoShading_Elevation_end.state.toString()+ ") ")
               return;
            }

            if (returnvalueWest == false && returnvalueEast == false && returnvalueSouth == false) {
                if (log) logInfo(filename, logPrefix + "Beende, da heute noch keine automatische Abschattung stattfand")
                return;
            }

            if (returnvalueWest == true || returnvalueEast == true || returnvalueSouth == true) {
                            // Rollladen öffnen
                            if (log) logInfo(filename, logPrefix + "Rollladen werden geoeffnet")
                            gRollladen.allMembers.forEach[blind|
                                if((blind.state as Number).intValue == targetHights.get(blind.name.toString()).intValue) {
                                    if (log) logInfo(filename, logPrefix + "Oeffne Rollladen: " + blind.name.toString())
                                    blind.sendCommand(UP)
                                    //Pushnachricht versenden                        
                                    sendPushoverMessage(pushoverBuilder("Abschattung für (" + blind.name.toString() + ") beendet, Rollladen wird geöffnet."))
                                } else {
                                    if (log) logInfo(filename, logPrefix + "Rollladen: (" + blind.name.toString() + ") wird nicht geoeffnet, da dieser zwischenzeitlich manuell verändert wurde.")
                                }    
                            ]                         
                            // Letzte Ausführung mit entsprechendem Zeitstempel belegen
                            AutoShading_End_last.postUpdate(now.toString())
                            
                            //Variablen zurücksetzen
                            returnvalueWest = false
                            returnvalueEast = false
                            returnvalueSouth = false
            }
        }            
end

Best reagrds

1 Like