How to concatenate parameters in command that I should send out

Hello to all,

I’m trying to make multiple room cleaning with Roborock vacuum, but with custom selection of rooms.
So idea is to have switch for every room and depending of which rooms are switched on command for room cleaning should be generated.

The command that I need to generate is in this format:
app_segment_clean[id1, id2,…id(n)] where id(n) are ids of rooms (number)

app_segment_clean[16, 17, 21] - if I want to clean that three rooms in one pass

If I want to only clean one room, command should be: app_segment_clean[18]

So, let say I have 8 rooms, then I create 8 switches for every room:

Switch room1 “Kitchen” (gVacRoom)
Switch room2 “Living room” <living_room> (gVacRoom)
Switch room3 “Corridor” (gVacRoom)
Switch room4 “Bedroom” (gVacRoom)
Switch room5 “KidsRoom” (gVacRoom)
Switch room6 “WorkingRoom” (gVacRoom)
Switch room7 “Bathroom1” (gVacRoom)
Switch room8 “Bathroom2” (gVacRoom)

And one master switch to start vacuuming:

Switch startCleaning “Start cleaning” (gVacRoom)

Every room have some ID, let say for example purpose that IDs are from 11 to 18

So now I need to generate command for vacuuming based on which room switch is selected.

If I select Kitchen, Livingroom, Bedroom and Working room, command should be:

How to acomplish that?

See Design Pattern: Encoding and Accessing Values in Rules for a number of techniques of embedding the ID of the room with the Item.

See Design Pattern: Working with Groups in Rules for all sorts of things you can do with Groups.

Given those, let’s say you’ve encoded the ID in the Item name, as you have kind of done already.

Then you can do something like

val rooms = gVacRoom.members.filter[ room | room.state == ON ].map[ name ].reduce[ ids, name | ids = ids + ", " + name.replace("room", "") ]

I just typed in the above and there might be typos or the like, but it along with the two design patterns should get you going.

Thanks for pointing me in that way.
I’m not so advanced OpenHAB user so I admit that I barely understand this members.filter, maps, reduce commands, never used it before. I have also suprised that this can be solved so simple, almoust in one line in rules. Or am I missing something?

By your example I have corrected room names so names are now like room18, room21 where number represent id that I should put in command. I put them in gVacRoomList group:

Group  gVacRoomList "List of rooms to clean" (gVacRoomList)
Switch room18 "Kuhinja" <kitchen> (gVacRoom,gVacRoomList)
Switch room21 "Dnevna" <living_room> (gVacRoom,gVacRoomList)
Switch room19 "Hodnik" <room> (gVacRoom,gVacRoomList)
Switch room16 "Radna" <room> (gVacRoom,gVacRoomList)
Switch room22 "Spavaca" <bedroom> (gVacRoom,gVacRoomList)
Switch room26 "Beba" <baby> (gVacRoom,gVacRoomList)
Switch room17 "Kupaona1" <bathroom> (gVacRoom,gVacRoomList)
Switch room23 "Kupaona2" <bathroom> (gVacRoom,gVacRoomList)
Switch startCleaning "Start cleaning" (gVacRoom)

So in rules I put this:

rule "Switch vacuum on"
	Item startCleaning changed to ON

	val rooms = gVacRoomList.members.filter[ room | room.state == ON ].map[ name ].reduce[ ids, name | ids = ids + ", " + name.replace("room", "") ]

But than in logs I can see this:

Rule ‘Switch vacuum on’: The name ‘ids’ cannot be resolved to an item or type; line 7, column 48, length 3

So I add “val ids” in rule and now command is executed, but with null as ids:

2020-12-17 20:18:52.200 [vent.ItemStateChangedEvent] - startCleaning changed from OFF to ON
2020-12-17 20:18:52.346 [ome.event.ItemCommandEvent] - Item ‘roomCleaning’ received command app_segment_clean[null]
2020-12-17 20:18:52.349 [nt.ItemStatePredictedEvent] - roomCleaning predicted to become app_segment_clean[null]

What the filter, map and reduce do is explained in the Working with Groups in Rules DP post.

The error is “ids” should be “rooms”. It’s always a good idea to log stuff out.

The first line saves a list of the room ID numbers for all the rooms that are ON as a comma separated list. The second one sends the command to the Item that controls the robot.

I know what roomCleaning.sendCommand but don’t understand how this ids are filtered by

val rooms = gVacRoomList.members.filter[ room | room.state == ON ].map[ name ].reduce[ ids, name | ids = ids + ", " + name.replace(“room”, “”) ]

As I understand, this part of command:
gVacRoomList.members.filter[ room | room.state == ON ]
should filter rooms that are “switched on”

If I undestand this correctly, here “room” means as generic identifier of switch items that are members of gVacRoomList. So if forst first two rooms are switched on - room18 and room21 result of this filtering would be names of that two switches and that are room18 and room21.

It is now unclear to me what .map[ name ] do, as per documentation it pulls out name of each item that is member of group…so why is that used when we allready pulled names room18 and room21?

After that, I’m totally lost with reduce[ ids, name | ids = ids + ", " + name.replace(“room”, “”) ] as per documentation:

MyGroup.members.reduce[ <result>, <value> | <result> <operation> <value> ]: returns the reduction of all the values in the Set by the <operation>, for example MyGroup.members.reduce[ sum, v | sum + v ]will return the sum of all members of MyGroup

I’m totally lost what should be result, value, operation…

This part “name.replace(“room”, “”)” I suppose that replaces word room from item name so from room18 I will get only 18, which is id that I need.

ids = ids + ", " + name.replace(“room”, “”) should build set of ids, so for first two rooms shoud be “18,21”

As you said “The error is “ids” should be “rooms”” that actually meand that vali command should be:

val ids = gVacRoomList.members.filter[ room | room.state == ON ].map[ name ].reduce[ ids, name | ids = ids + ", " + name.replace("room", "") ]

Now when switching on switch to send command out I’ve got this error:
[ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule ‘Switch vacuum on’: Couldn’t invoke ‘assignValueTo’ for feature param ids

I figured out that if I remove “ids=” in reduce parameter now command works, but in first member name word “room” is not removed

ids = gVacRoomList.members.filter[ room | room.state == ON ].map[ name ].reduce [ ids, name | ids + ", " + name.replace("room", "") ]


Output of command is:

app_segment_clean[room19, 22, 26, 23]

So I have done this:

rule "Switch vacuum on"
	Item startCleaning received command ON


	val ids = null	
	val command = null
	ids = gVacRoomList.members.filter[ room | room.state == ON ].map[ name ].reduce [ ids, name | ids + ", " + name.replace("room", "") ]
	command = ids.replace("room","")

And now output command looks OK:

`app_segment_clean[19, 22, 26, 23]`

Thanks for idea how to solve this!

val rooms =

We are creating a variable named rooms

val rooms = gVacRoomList.members

Get the list of all the members.

val rooms = gVacRoomList.members.filter[ room | room.state == ON]

Get a list of the members that are ON. At this point we have a list of Items.

val rooms = gVacRoomList.members.filter[ room | room.state == ON].map[ name ]

Convert the list of Items to a list of Strings of the Items names. So at this point we have a list of all the Item names, no more Items.

val rooms = gVacRoomList.members.filter[ room | room.state == ON ].map[ name ].reduce[ ids, name | ids = ids + ", " + names.replace("room", "") ]

Convert the list of Item names to a single String that consists of just the number part of the Item names, separated by commas.

So after the reduce, rooms should look something like 18, 19, 22, 26. That’s what you want to send as part of your command to the robot, the list of room numbers to clean.

Because we didn’t pull the Item names. We pulled the actual Item object. But all we want is the Item name so we convert the List of Items to a List of Strings of the Item names. It happens after the filter so the make creates a list of only the Item names that are ON.

reduce takes a list of something and reduces it down to just one something. In this case it creates a single String that is a comma separated list of just the number portion of the Item names. In your example it should be “18, 21”.

That’s why I gave the hint to log out stuff. If you log out rooms you will see exactly what that long line produced.

I should have been more specific.


Ah, yes. Let’s move the removal of “room” to the map instead.

val ids = gVacRoomList.members.filter[ room | room.state == ON ].map[ name.replace("room", "") ].reduce [ ids, name | ids + ", " ]

The reduce function needs to initialize the return variable, in this case ids. It does so by initializing it with the first element of the list passed to it from the map. But because of that, we never got a chance to remove “room” from the name for that first element. By moving the replace to the “map” we avoid that problem.


    val ids = null	
	val command = null

This should be generating all sorts of warnings in the logs. The whole point of using val instead of var is that you are telling the program that "ids is a constant, don’t let me change it." So you should never see ids anywhere else in your rule with a single = after it.

The second problem is because you’ve initialized it to null there is absolutely no information the Rules Engine can use to determine the type of those values. So it’s going to fall back to the lowest common denominator, Object. That doesn’t matter here because even Object has a toString so it can figure out how to do the string operation to build the command. But if ids were a number or an Item or a timer it would not work.

1 Like

Thank you very much for detailed explanation!

Hovever, there is one more little mistake that you made, at the end it is missing “+ name” as now command produces output “app_segment_clean[19, , ]”

So command that works fine looks like this:

val ids = gVacRoomList.members.filter[ room | room.state == ON ].map[ name.replace("room", "") ].reduce [ ids, name | ids + ", " + name ]

Thanks again!

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.