Rule DSL anonymous "functions"

Hey guys,

I´m coming from typescript and I can´t figure out how to do it in Xtend:

rule “NotifyPowerChanges”

when

Item PowerA_ErdgeschossKuecheKuehlschrank changed or

Item PowerA_KellerTechnikraumKuehlschrank changed or

Item PowerA_ErdgeschossWaschraumGefriertruhe changed

//Time cron "0 0/1 * * * ?"

then

var mText = ""



val addText = [SwitchItem item |

mText = mText.concat("\n"+item.getName()+": "+item.state.toString()+" (lastupdate: "+item.lastUpdate?.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+")")

]



addText(PowerA_ErdgeschossKuecheKuehlschrank)



var item = PowerA_ErdgeschossKuecheKuehlschrank    

mText = mText.concat("\n"+item.getName()+": "+item.state.toString()+" (lastupdate: "+item.lastUpdate?.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+")")

item = PowerA_ErdgeschossWaschraumGefriertruhe    

mText = mText.concat("\n"+item.getName()+": "+item.state.toString()+" (lastupdate: "+item.lastUpdate?.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+")")

item = PowerA_KellerTechnikraumKuehlschrank    

mText = mText.concat("\n"+item.getName()+": "+item.state.toString()+" (lastupdate: "+item.lastUpdate?.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+")")

val mailActions = getActions(“mail”,“mail:smtp:bitpoint”)

mailActions.sendMail(“my@address.de”, “OH changes in cooling”, mText)

I want to put the repeating line

mText = mText.concat("\n"+item.getName()+": “+item.state.toString()+” (lastupdate: “+item.lastUpdate?.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+”)")

in the anonymous function “addText” and call it for all Items, but It´s not working

Can someone point me in the right direction ?

Thanks :slight_smile:

Why not use the JS Scripting add-on which will be closer to what you are used to. Rules DSL is nothing but frustrating for people who already know how to program.

Rules DSL has lambdas. A lambda is denoted like:

[ arg1, arg2 |
    // code goes here
]

You can save that to a variable/value.

val myProcedure = [ |
    // code goes here
]

val myFunction [ arg1, arg2 |
    // code goes here
]

The return value is determined by the last line evaluated in the // code goes here. So if you have multiple “exits” to the function do to branching, make sure they all are of the same type.

A lambda will inherit the context that exists when it is defined. If you create a lambda as a global (i.e. before the rules start, sometimes erroneously called “globals”) there is no context so you’ll have to pass in everything you need as an argument. There is a limitation of up to only seven arguments.

To call a lambda (the part you are missing) you have to use myFunction.apply(arg1, arg2).

That addresses the question, now let’s address the XY Problem. A rule like this is often better to implement using Groups and streaming.

First put all your Power Items into a Group (we’ll call it PowerA) and trigger the rule using Member of Power changed.

Then use a map/reduce.

rule "NotifyPowerChanges"
when
    Member of PowerA changes
then
    val mText = PowerA.members
                      .map[item | item.name+": "+item.state+" (lastupdate: "+item.lastUpdate?.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+")"]
                      .reduce[m, str | m + str + "\n"]

    val mailActions = getActions(“mail”,“mail:smtp:bitpoint”)

    mailActions.sendMail(“my@address.de”, “OH changes in cooling”, mText)
end

In JS Scripting using the openhab-js library (which comes with the add-on) it would look either like this:

rules.JSRule({
  name: "NotifyPowerChanges",
  description: "Sends an email when a power Item changes",
  triggers: [triggers.GroupStateChangeTrigger("PowerA")],
  execute: data => {
    const mText = items.getItem("PowerA").members
                                         .map(item => item.getName()+": "+item.state.toString()+" (lastupdate: "+item.lastUpdate?.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+")")
                                         .reduce(function(text, item) { text + item + "\n");
    const mailActions = actions.Things.getActions("mail", “mail:smtp:bitpoint”);
    mailActions.sendMail(“my@address.de”, “OH changes in cooling”, mText);
  }
});

or using the fluent rule builder

rules.when().memberOf('PowerA').changed().then(data => {
  const mText = items.getItem("PowerA").members
                                       .map(item => item.getName()+": "+item.state.toString()+" (lastupdate: "+item.lastUpdate?.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+")")
                                       .reduce(function(text, item) { text + item + "\n");
  const mailActions = actions.Things.getActions("mail", “mail:smtp:bitpoint”);
  mailActions.sendMail(“my@address.de”, “OH changes in cooling”, mText);  
}).build('NotifyPowerChanges');

In all of these cases I just typed the code in. There are likely typos.

Man, that was one of the best responses I ever got to a post :smiley:
Many thanks for this!

Is there a good ide support (e.g vs code) for the javascript add-on ? (incl. language server?)

It´s cool, atm, to get the current state of an item when you hover it in vs-code.

If not, not too bad :slight_smile: … being able to code it in js / typescript is a huge advantage (for me at least). I´ll have a look to this asap :slight_smile:

Thanks again!

Depends on what you want. It’s mostly pure JavaScript so you can get all the linting and auto-completion and such that you’d get for any JavaScript. And since most if not all of your interactions with OH will be through openhab-js it’s pretty good. However, there is no language server so you won’t get Item names and stuff like that.

I do all my rules in the UI because I want to be able to publish rule templates which only support UI based rules and I’ve not been too disappointed. It’s not the best but not the worst and the average rule is usually relatively simple anyway.

Be sure to look at the reference in the JS Scripting add-on docs and be aware that there are a few of us who have published openHAB libraries on NPM. My library is openhab-rules-tools which mostly focuses on timer management and timing type stuff. Some of the more generic stuff I’m moving over to openhab-js itself, like timeUtils. Libraries are not supported in Rules DSL at all.