Need help with rule output

Hi,
Need help with “beautification” of the rule output.
Or pointers to that part of the documentation, as this advanced string manipulation is beyond my skills…

Found this old rule from @rlkoshak

rule "Low battery alert"
when
    Item gBatteries changed
then
    if(gBatteries.state > 20) return;

    val lowBatteriesList = gBatteries.members.filter[ b | b.state < 20 ].map[ name ].reduce[ list, bat | list = list + ", " + transform("MAP", "batteries.map", bat) ]
    aAlert.sendCommand("The following batteries need to be replaced: " + lowBatteryList.subString(0, lowBatteryList.size-2) + ".")
end

stole it and simplified (omit using map file, plus removed some other parts)

rule "Low battery alert"
when
    Time is midnight
then
    if(Ephemeris.isWeekend == true)
      {val lowBatteryList = gBattery.members.filter[b|b.state <= 15].map[name]
      mailActions.sendMail("vanja@something.net", "openHAB Alert - Battery Low", "Low battery at: " + lowBatteryList)}
end

the email I get:

Low battery at: [FridgeBattery, LivingBattery, Balcony2DoorBattery]

the email i would like to get:

Low battery at: 
FridgeBattery x%
LivingBattery y%
Balcony2DoorBattery z%

(lose the [brackets], put each sensor in a new row, add item state to each)

It’s the “other parts” that converts the list to a String, namely the reduce. If you are going to copy code it’s important to understand what it does before you change it. You can find more information on map/reduce at Design Pattern: Working with Groups in Rules

The map operation takes each element of the list, does something to it, and returns a new list with the results. The reduce takes each element of the list and combines them into a single result.

    val lowBatteryList = gBattery.members.filter[b|b.state <= 15]
                                         .map[ i | i.name + ' ' + i.state ]
                                         .reduce[ msg, b | list = b + '\n' ]
    mailActions.sendMail("vanja@something.net", "openHAB Alert - Battery Low", "Low battery at:\n" + lowBatteryList)

In JS this is simpler since we have the join operator. Note, you should always get a Thing action inside the rule that uses it. When you get the Action as a global, if the Thing isn’t online when the .rules file is read or if the thing goes offline then back online the action will fail until you reload the .rules file.

rules.when().timeOfDay("00:00")
     .if(() => actions.Ephemeris.isWeekend())
     .then( () => 
       const bats = items.gBatteries.filter( i => i.numericState < 15)
                                    .map(i => i.name + ' ' + i.state)
                                    .join('\n');
       actions.get("mail", "uid:of:mail:thing")
              .sendMail("vanja@something.net",
                        "openHAB Alert - Battery Low",
                        "Low battery at:\n" + bats)
     )
     .build('Low battery alert', 'Sends an email with the list of all the low batteries.', [], 'lowBatteries');

Note the above is using rule builder.

thanks for clarification!
with this rule i get:

2025-01-28 20:35:38.061 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'failsafe-10' failed: Couldn't invoke 'assignValueTo' for feature JvmVoid:  (eProxyURI: failsafe.rules#|::0.2.9.2.0.0.1.0.0.2.7.0.1.0.0::0::/1) in failsafe

Copy and paste error.

reduce[ msg, b | msg = b + '\n' ]

still doesnt run

2025-01-28 20:53:00.559 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configura using it anyway:
Assignment to final parameter

2025-01-28 20:54:22.633 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'f invoke 'assignValueTo' for feature param msg in failsafe

This is why I am basically done with Rules DSL. It’s a broken language. No one should be doing new development of rules using it.

I don’t know what’s wrong unless you’ve already defined msg as a val somewhere else in this rule or as a global. The first argument to reduce is the “accumulator” and the second argument is the current element of the list. :man_shrugging:

OK clear, thanks, will try out java method!

got it working with

.reduce[ msg, b | msg + b ]

(did the new line \n before in .map part
1 Like