Design Pattern: Working with Groups in Rules

Just insert your group items where you want check it, like this:

items:
items1 (gGroup)
items2 (gGroup)
items3 (gGroup)

rule:

if (gGroup.toString == "OFF") {

}

There are no issue using group items like “normally” items.

that’s pretty simple :joy:
thanks!!!

You’d need to give your Group a type, and an aggregation function, for it to take up a state related to its members
e.g. Group:Switch:OR(ON,OFF)

Remember it is the state you are probably interested in
if (gGroup.state == OFF) {

1 Like

Hi,
I’m trying to get a complete local copy from a group item in a rule so I can perform operations on this group without affecting the global group in my item file.

I tried several ways to get copies of my group item but none of was working.
So I tried filtering the group item and assigning it to a new variable like it could be done with findFirst but that didn’t work either and unfortunately I couldn’t find any working example.

(the following code is not working but they were part of my journey on the way to despairing :grinning:)

val GroupItem sceneItems = Scene_Items.members

val sceneItems = Scene_Items.members.filter[ grp | grp.name.contains(itemNamePrefix+sceneRoomString) ]

So as I spent many hours with kind of trial and error without any success I’d like to ask you if you know any solution for my problem. :slightly_smiling_face:

What kind of operations? There isn’t much you can “do” to a Group. You can send it commands, which don’t affect it, but get passed to members.

This should work fine, as long as all of the members of Scene_Items are groups, although sceneItems is not a GroupItem but Iterable. You can’t create an Item in the rules DSL… you’d need scripted automation for that. The Jython helper libraries make this easier.

val sceneItems = Scene_Items.members.filter[GroupItem group | group.name.contains(itemNamePrefix + sceneRoomString)]

What errors are you getting? It is much better to describe what you would like to do, rather than ask for help on how to implement a particular solution. There may be easier ways to do what you are trying to accomplish. Take a look at #8.

For example, this may provide everything you’re looking to implement…

Thanks for your answer!

I think that is what I unintentionally tried to do.
For now I created a new group item in an items file and this group item gets cleared every time I call the rule so I can then add new members from my ‘Scene_Items’ group. This solves my problem for now but I think in the long run I have to (and will) make use of the Jython helper libraries you mentioned.

1 Like

Hey @rlkoshak and others…

i try to simplify my rules for the OpenSprinkler Telegram rules i wrote to get the label names from the items file… and also to get the buttons in the amount of stations that are not OFF

OLD part:

val OSPI_ReplyId = "OSPI_Reply"
var StationDuration = 600 // 10min
val Station01 = "Rasen oben"
val Station02 = "Rasen Hang"
val StationOFF = "Alle aus"

rule "Telegram Bot receive rasen"
when
    Item TelegramBotLastMessageDate received update
then
    val telegramAction = getActions("telegram","telegram:telegramBot:bot1")

    if (TelegramBotLastMessageText.state.toString.toLowerCase == "/rasen") {
       telegramAction.sendTelegramQuery(Long::parseLong(TelegramBotChatId.state.toString), "*Beregnungs-Optionen:*\nWelcher Bereich soll bewässert werden?", OSPI_ReplyId, Station01, Station02, StationOFF)
    }
end

New Part:

val OSPI_ReplyId = "OSPI_Reply"
var StationDuration = 600 // 10min
val StationOFF = "Alle aus"

rule "Telegram Bot receive rasen"
when
    Item TelegramBotLastMessageDate received update
then
    val telegramAction = getActions("telegram","telegram:telegramBot:bot1")
    val reply_text = "*Beregnungs-Optionen:*\nWelcher Bereich soll bewässert werden?"

    val reply_buttons = gOSPI_Station.members.filter[ i | i.state == OFF ].map[ label ]
    logInfo("OSPI_Reply", reply_buttons.toString)

    if (TelegramBotLastMessageText.state.toString.toLowerCase == "/rasen") {
       telegramAction.sendTelegramQuery(Long::parseLong(TelegramBotChatId.state.toString), reply_text, OSPI_ReplyId, reply_buttons)
    }
end

Now here comes the part where i could need some help…

the reply_buttons i generated in the correct amount of stations… and for each name it is created the button in the telegram chat…
2020-08-19 17_14_02-Telegram

Now i want to add the val StationOFF to that String so that this button is also generated…
any tips on how to achieve that?

One approach could be to create a dummy item and also add it to the group gOSPI_Station, but i want to avoid that.

Thanks
/Holger

I’m not entirely certain what you are asking for here. I don’t use Telegram so don’t have a context. Are you asking to have two strings for each station, one to turn it on and one to turn it off?

If so you probably can’t use the map and instead need to use a for loop to build up the List manually.

val reply_buttons = newArrayList
gOSPI_Station.members.filter[ i | i.state == OFF ].forEach[ s |
    reply_buttons.add(s.label)
    reply_buttons.add(s.label + " OFF")
]
1 Like

Rich, thanks for your help.

No not 2 strings:::

my line
val reply_buttons = gOSPI_Station.members.filter[ i | i.state == OFF ].map[ label ]
is resluting in this
2020-08-19 18:07:31.988 [INFO ] [se.smarthome.model.script.OSPI_Reply] - [Station 1, Station 2]

Station 1, Station 2 are the labels of current gOSPI_Station members (which will be more in the future)

and beside these Station names… I also want to append the String with “All OFF”

how can i add this to the val reply_buttons

...map[ label + " All OFF" ]

nope… i also tried this… this is resulting in
[Station 1All OFF, Station 2All OFF]

But i want

[Station 1, Station 2, All OFF]

Underneath


can’t you just do:

reply_buttons = reply_buttons + ", All OFF"

Nope - see #145

this gives me a error in VS Code
Assignment to final variable(org.eclipse.xtext.xbase.validation.IssueCodes.assignment_to_final)

As I said, it was really unclear what you were trying to accomplish.

Just add another element.

reply_buttons.add("All OFF")

That won’t work because reply_buttons is a List Object and there is no + operator for List objects. But there is an add method. Or maybe it’s append. I haven’t done Rules DSL in a long time.

Does it work though? Does openHAB itself give an error. I find that VSCode doesn’t always tell the whole story with openHAB stuff… See above why it won’t work!

sorry that i was not clear enough :slight_smile:

this gives me also a error:
The method add(String) is undefined for the type Iterable<String>(org.eclipse.xtext.diagnostics.Diagnostic.Linking)

OK, the map returns an Iterable. I’m going to guess that underneath it’s a List of some sort so cast it to a List. You’ll have to import java.util.List.

import java.util.List

val List<String> reply_buttons = (gOSPI_Station.members.filter[ i | i.state == OFF ].map[ label ]) as List<String>
reply_buttons.add("All OFF")

If that doesn’t work you need to use the forEach approach above to manually add each element to a List and then append “All OFF”.

1 Like

this gives me a error
Rule 'Telegram Bot receive rasen': Could not cast [Station 1, Station 2] to java.util.List; line 18, column 38, length 80

So for now i created a dummy item:
Switch OpenSprinklerAllStationsOff "Alle aus" (gOSPI_Station)

and kept that line in the rules:
val reply_buttons = gOSPI_Station.members.filter[ i | i.state != ON ].map[ label ]

2020-08-19 18_51_09-Telegram

this is the result i was searching for… but requieres such dummy item… but i can live with that… maybe this will help me with the second part of the rule for the telegram answer :slight_smile:

Thanks all for you help :slight_smile:

Hi, I am just trying to improve the presence detection in my house.
I would like to detect if a light has been switched since last 10 minutes but to avoid ton’s of rows I would like to use a group function.

All the Items are in a “Luci” Group

A single row function which works is for example:

var Number Presenza=0
if (Luce_1.changedSince(now.minusMinutes(10))) Presenza=1
if (Luce_2.changedSince(now.minusMinutes(10))) Presenza=1

Now Can you Help with the group Function?

I tried:

if (Luci.members.filter[ i | i.changedSince(now.minusMinutes(10)) ]) Presenza=1

But doesn't work as expected (no errors but always Presenza=0 even if I switch a light)

Any help?

Thanks
Lorenzo