Hi,
I finally got my vacuum (roborock) to accept the command to clean a specific room in a multi level environment.
Doing so, I had two problems:
- I can only send the command to clean room 16, room 17 … As every floor has its own map starting with room 16, it is impossible to distinguish between “room 16” in ground floor and “room 16” in second floor
- The binding do not tell which map it is using.
So in order to get this working you have to determine:
- on which floor is the vacuum
- on which floor is the “to be cleaned” room
- what is the correct room number
Task 1 is done using the method from here … so please check there in order to understand this part. Thanks to @tommycw10.
With the information from there you have to create a map file (in my case Rocky_Etage.map) with the information which ID of room 16 corresponds to which floor:
838001020681=EG
838001020689=UG
I named the different floors with two letters as e.g.
- EG is ground floor (german: Erdgeschoss)
- UG is cellar (german: Untergeschoss)
Additionally you have to find the room numbers for you room by try and error. Send a command over OpenHAB to clean a room and check in the App of the vacuum (not OpenHAB) which room is scheduled for cleaning.
With this information you have to fill the map-file. It will look like this:
838001020681=EG
838001020689=UG
EG_BU=19
EG_ES=21
EG_KU=18
The rooms also have two letter names as e.g.
- BU is office (Büro)
- ES is dining room (Esszimmer)
- KU is kitchen (Küche)
- …
Now you need to have
- a switch for each room you want to clean
- one group with all these switches
- a string for the level where the vacuum really is
- one proxy
Group:Switch:OR(ON,OFF) gSaugSwitches "Group with one switch for each room"
Switch RockyEtagensuche
String Rocky_Etage "The current level"
Switch HH_UG_GZ_Room1 (gSaugSwitches)
Switch HH_UG_KE_Room2 (gSaugSwitches)
...
The switches have to be named as above:
- HH is the name of the building (example)
- UG is the name of the level (example)
- GZ and KE are names for the rooms (examples)
- everything after the third “_” is up to you
The rules are:
// this is to en- or disable debugging of a specific set of rules.
val logName = "Rocky" // Name of the set of rules
var loggingInfo = true // if loggingInfo =true , this set of rules can be debugged without getting debug-messages from every rule.
var Timer Pause_Timer = null // the vacuum is sometimes slow and need a rest before getting new commands
rule "Vacuum should go to work"
when
Member of gSaugSwitches received command ON
then
// For debugging: Set the name of this rule
val ruleLogName = "Vacuum should go to work (" + triggeringItemName + ") "
// This is how a debuglog looks like.... I also use this for documentation of the code
if (loggingInfo == true ) logInfo(logName, ruleLogName )
// This rule starts if one switch in the group receives the command to clean a specific room
// If the current floor is know, the rule will skip a lot of steps
if ( Rocky_State.state == "Charging" ) {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Vacuum in Dock => Level is known")
Rocky_Etage.postUpdate("EG")
return
}
// Proxy to tell OpenHAB to check the current level. No Command (only update), as a command would directly trigger next rule. Using update, the next rule will be triggered after a map-update
RockyEtagensuche.postUpdate(ON)
// Start vacuum. The vacuum will automatically start positioning.
// After positioning (or at least after starting cleaning) this will automatically result in a new map which will trigger next rule
Rocky_VacuumOnOff.sendCommand(ON)
end
rule "A new map is available"
when
Item Rocky_CleaningMap changed or // everytime the map changes (which is often)
Item RockyEtagensuche received command ON // If want to know where the vacuum is.
then
val ruleLogName = "A new map is available (" + triggeringItemName + ") "
// A new map is available. I the floor has to be determined, we need the room_mapping.
// The rule will ask for a room_mapping and another rule will wait for the result
if (RockyEtagensuche.state == ON ) {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "New Map and openhab should check for Level - I get the Floorplan")
Rocky_ExecuteCommand.sendCommand("get_room_mapping")
// The level-check should only be done once
RockyEtagensuche.postUpdate(OFF)
}
end
rule "When room_mapping is available"
when
Item Rocky_ExecuteCommand changed from get_room_mapping
then
val ruleLogName = "room_mapping is available: "
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Triggered. The room_mapping is " + newState.toString)
var String firstRoom = transform("JSONPATH", "$.result[0][0]", newState.toString)
if (loggingInfo == true ) logInfo(logName, ruleLogName + "First room on room_mapping is " + firstRoom)
// first room should be 16
if (firstRoom == "16"){
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Check the ID of Room 16 (unique for each floor) and use map in order to find floor")
var Etage = transform("MAP", "Rocky_Etage.map", transform("JSONPATH", "$.result[0][1]",newState.toString))
Rocky_Etage.postUpdate(Etage)
} else {
Rocky_Etage.postUpdate("UNDEF")
}
end
rule "Go to your room"
when
Item Rocky_Etage received update
then
// We know the current floor - now we check if the room in in this floor and then go to this room
val ruleLogName = "Go to your room "
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Triggered")
// If there is a command to clean a room
if (gSaugSwitches.state == ON) {
// stop cleaning for new instructions
Rocky_ControlVacuum.sendCommand("pause")
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Check which switch was used latest")
val WelcherSaugSwitch = gSaugSwitches.members.filter[i|i.state==ON].sortBy[lastUpdate].last
if (loggingInfo == true ) logInfo(logName, ruleLogName + "found: " + WelcherSaugSwitch.name)
// get level and room from name of the switch
val String SollEtage = WelcherSaugSwitch.name.toString.split('_').get(1)
val String SollRaum = WelcherSaugSwitch.name.toString.split('_').get(2)
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Level: " + SollEtage + "; Room: " + SollRaum)
// only do the cleaning once
WelcherSaugSwitch.postUpdate(OFF)
//if vacuum is in correct level
if ( SollEtage != Rocky_Etage.state.toString ) {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Falsche Etage. Ist= " + Rocky_Etage.state + ", Soll: " + SollEtage )
Rocky_VacuumOnOff.sendCommand(OFF)
return
}
// get room ID from MAP
val RaumNummer = transform("MAP", "Rocky_Etage.map", SollEtage + "_" + SollRaum)
Pause_Timer = createTimer( now.plusSeconds(3) ,[|
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Go to your room " + RaumNummer)
Rocky_VacuumRoom.sendCommand(RaumNummer)
])
} else {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Vacuum - your fine... go on")
}
end
Thanks to everybody on this community. I learned everything here but I am not able to say where exactly.
Best regards
Peter