.members.filter DSL rule help request

Hey dear DSL experts,

Currently I’m working on a personal reminder for specific low price information. I am failing with the .members.filter functions.

The items are:

Switch	Home_Dummyswitch							"Dummyswitch"			<switch>	(home)
Switch	ALL_PetrolStations_E5_Remind				"Remind E5 Prices"		<switch>	(home)
Group	ALL_PetrolStations_E5						"All E5 Prices"			<price>		(home)
Number	PetrolStation_AGIP_Wallensteinstr_E5		"Wallenstein E5"		<price>		(ALL_PetrolStations_E5)
Switch	PetrolStation_AGIP_Wallensteinstr_OpenState	"Wallenstein Opened"	<switch>	(home)
Number	PetrolStation_JET_Gebersdorferstr_E5		"Gebersdorfer E5"		<price>		(ALL_PetrolStations_E5)
Switch	PetrolStation_JET_Gebersdorferstr_OpenState	"Gebersdorfer Opened"	<switch>	(home)

Persistence is running successfully for all PetrolStation_*_E5 items.
Of course, there are more *_E5 and *_OpenState items, but its not helpfull to list all here.

The DSL rule I wrote looks like:

rule "E5_LowPriceRemind"
	Item Home_Dummyswitch changed
	if(ALL_PetrolStations_E5_Remind.state == ON) {
		var String LowPriceStationE5 = ""
		ALL_PetrolStations_E5.members.filter[y|y.name.replace('_E5', '_OpenState').head.state == OPEN].filter[y|y.state < y.averageSince(now.minusDays(7))].forEach[y|
				LowPriceStationE5 += y.name.replace('_', ' ').toString
				LowPriceStationE5 += ": "
				LowPriceStationE5 += y.state.toString
				LowPriceStationE5 += "\n" 
		if(LowPriceStationE5 != "") {
			logInfo("FILE", "Reminder: currently these 7d-E5-Lowprices:" + "\n" + LowPriceStationE5)

within first .filter I try to reduce to stations which are opened currently. My plan is to filter the _E5 items, and for each check the derived _OpenState item.

within second .filter I try to reduce to stations which have currently lower price then in the average of the last 7 days.

Both conditions within the .filter seem to fail, if I use just one of them.
What could be a working solution?

  1. How can I filter for one item and then check the state of a derived item?
  2. Is it possible at all to use the .averageSince within the condition?

Your assistance is very welcome :slight_smile:
thanks already ahead

You can’t just build a String that is the name of an Item and get that Items state. When you build the String all you have is a String and a String doesn’t have a state. What you need is the Item. See Design Pattern: Associated Items for options.
Furthermore I have no idea what the head in the filter is all about.

There are map and reduce functions that are used for this. See Design Pattern: Working with Groups in Rules for examples.

And you cannot modify a variable outside of a forEach lambda from inside the lambda. You can only access vals and vals cannot be changed. So you can’t reduce the list to a String using String concatenation. Thus you really do need to use the map/reduce methods or use some other data structure such as a StringBuilder which you can define as a val and then call append to add more text to it.

import org.eclipse.smarthome.model.script.ScriptServiceUtil

    // Get the open stations with meaningful error log statements
    val openStations = AllPetrolStations_E5.members.filter[ y | 
        ScriptServiceUtil.ir.getItem(y.name.replace('_E5', '_OpenState')).state == OPEN
    val avgLess = openStations.filter[ y |
        y.state < y.averageSince(now.minusDays(7))

    val LowPriceStationE5 = avgLess.reduce[ str, y | str += y.name.replace('_', ' ') + ": " + y.state + "\n" ]

I’m not 100% positive on the above but the linked to design patterns should help. I mostly use Jython rules these days, here is a notional version in Jython.

from core.rules import rule
from core.triggers import when
from core.actions import PersistenceExtensions

@when("Item Home_Dummyswitch changed")
def low_price(event):
    if items["ALL_PetrolStations_E5_Remind"] != ON:

    openStations = [ y for y in ir.getItem("ALL_PetrolStations_E5").members if items[y.name.replace('_E5', '_OpenState')] == OPEN ]
    avgLess = [y for y in openStations if y.state < PersistenceExtensions.averageSince(y, DateTime.now().minusDays(7))]
    LowPriceStationE5 = ""
    for y in avgLess:
        LowPriceStationE5 = "{} : {}\n".format(y.name.replace('_', ' '), y.state)

    if LowPriceStationE5:
    low_price.log.info("Reminder: currently these 7d-E5Lowprices: \n{}".format(LowPriceStationE5)

Many thanks @rlkoshak. Your DSL suggestion works perfectly with a correction:
ScriptServiceUtil.getItemRegistry.getItem( ...
is to be used.

avgLess.reduce[ str, y | str += y.name.replace...
generates warning

Validation issues found in configuration model xyz.rules, using it anyway: Assignment to final parameter

but it works anyway.