Thanks to the updates of the binding, some rules are obsolete as the binding is doing the heavy lifting. The rules need the newest binding.
Please check earlier posts - there are many answers.
karaf
[Edit: Update of the binding allone is not possible anymore due to braking changes in OH 3.1. You have to install OH 3.1 (Milestone)]
update org.openhab.binding.miio https://ci.openhab.org/job/openHAB-Addons/lastSuccessfulBuild/artifact/bundles/org.openhab.binding.miio/target/org.openhab.binding.miio-3.1.0-SNAPSHOT.jar
Items
Switch Rocky_VacuumOnOff // linked to :actions#vacuum
String Rocky_State // linked to :status#state
String Rocky_ExecuteCommand // linked to :actions#rpc
String Rocky_Room16 // linked to :info#room_mapping with profile JSONPATH; Profile Configuration: $.[0][1]
Group:Switch:OR(ON,OFF) gSaugSwitches "Group with one switch for each room"
Switch HH_UG_GZ_Room1 (gSaugSwitches)
Switch HH_UG_KE_Room2 (gSaugSwitches)
And some Switches for the Zones
Switch HH_EG_ZO_esstisch (gSaugSwitches)
Switch HH_EG_ZO_arbeitsflaeche (gSaugSwitches)
String Rocky_Etage "The current level"
String Vacuum_Zone "The zone to clean"
String Rocky_Saugbefehle "Please clean..."
/* in the UI, you have to configure Rocky_Saugbefehle with stateDescription & Command options
EG_KU=Küche
EG_EZ=Esszimmer
EG_FL=Eingang
EG_GT=Gästetoilette
EG_BU=Büro
SP_UT=Unterm Tisch
EG_ZO_esstisch=unterm Tisch
EG_ZO_arbeitsflaeche=Küchenzeile
*/
map
838001020681=EG
838001020689=UG
838001020686=E1
838001020694=E2
EG_BU=19
EG_FL=24
EG_ES=21
EG_KU=18
EG_GT=17
EG_SA=16
EG_BUFLGT=19,24,17
esstisch=-8.9,2.2,2.8,2.5,1
arbeitsflaeche=-4.2,1.3,4.2,1.3,1
grundreinigung=esstisch,arbeitsflaeche
=UNDEF
rules
// 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
// Function to calcilate from m to values the vacuum understands
// see https://community.openhab.org/t/xiaomi-roborock-zone-cleanup-rule/55138/2?u=peterk
val Functions$Function1<String, String> getZoneCoordinates =
[zone |
var parameters = zone.split(',')
// Docking point start position
val double x = 25500.0;
val double y = 25500.0;
// Bottom (y) left (x)
val double b = Double::parseDouble(parameters.get(0)) * 1000.0 + x
val double l = Double::parseDouble(parameters.get(1)) * 1000.0 + y
// Top (y) right (x)
val double t = b + Double::parseDouble(parameters.get(2)) * 1000.0
val double r = l + Double::parseDouble(parameters.get(3)) * 1000.0
// Build zone coordinates (and number of times to scan)
val coordinates = String::format("[%.0f,%.0f,%.0f,%.0f,%s]", b, l, t, r, parameters.get(4));
coordinates
]
/////////////////////////////////////////////////////////////////////
// The rules to check for the floor each time when positioning is done
// The idea is from here https://community.openhab.org/t/xiaomi-roborock-room-clean-multi-level-map/108850/13
///////////////////////////////////////////////////////////////////
rule "Rocky_Room16 changed"
when
Item Rocky_Room16 changed
then
// update the current floor
Rocky_Etage.postUpdate(transform("MAP", "Rocky_Etage.map", newState.toString))
end
/////////////////////////////////////////////////////////////////////
// The rules to control the vacuum
///////////////////////////////////////////////////////////////////
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 ( " + triggeringItem.name + ") "
// This is how a debuglog looks like.... I also use this for documentation of the code
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Set the floor to UNDEF and start cleaning.")
// Start cleaning - this will trigger positionin if neccesary. If positioning is not neccessary, the bindig will update Room16 anyway
Rocky_VacuumOnOff.sendCommand(ON)
Pause_Timer = createTimer( now.plusSeconds(5) ,[|
// After leaving the dock (5 seconds)- ensure there is a change in the floor if there was already a change prior, this will do nothig bad
Rocky_Room16.postUpdate("[]")
])
end
// When the "check the floor" is done
rule "Go to your room"
when
Item Rocky_Etage changed
then
val ruleLogName = "Go to your room "
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Triggered; Rocky_Etage.state = " + Rocky_Etage.state )
if ( Rocky_Etage.state.toString == "UNDEF" ) {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "UNDEF - Nothing to do")
return
}
// If there is a command to clean a room
if (gSaugSwitches.state == ON) {
// stop cleaning for new instructions
if ( (Rocky_ControlVacuum.state != "pause" ) && (Rocky_ControlVacuum.state != "dock" ) ) {
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)
// only do the command once
gSaugSwitches.sendCommand(OFF)
// 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 + "Floor: " + SollEtage + "; Room: " + SollRaum)
//if vacuum is in correct floor.
if ( SollEtage == Rocky_Etage.state.toString ) {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Etage OK")
// if Zonecleaning was activated go to this rule and exit
if ( SollRaum == "ZO" ) {
val String SollZone = WelcherSaugSwitch.name.toString.split('_').get(3)
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Zonenreinigung von " + SollZone)
Pause_Timer = createTimer( now.plusSeconds(3) ,[|
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Los gehts zu Zone " + SollZone)
// Finally.... send to the Zone
Vacuum_Zone.sendCommand(SollZone)
])
} else {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Raumreinigung")
// OK... Roomcleaning it is
// 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)
// Finally.... send the room
Rocky_VacuumRoom.sendCommand(RaumNummer)
])
}
} else {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Falsche Etage. Ist= " + Rocky_Etage.state + ", Soll: " + SollEtage )
}
} else {
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Thank you for the information of the current floor.")
}
end
rule "Vacuum Zone control"
when
Item Vacuum_Zone received command
then
logInfo(logName, "Vacuum command: {}", receivedCommand.toString)
// The final command send to the vacuum
var String command = ""
// Get zone coordinates
var String zone = transform("MAP", "Rocky_Etage.map", receivedCommand.toString)
// We still don't know if this is single or multiple zone
var parameters = zone.split(',')
try {
// Single zone only have numbers
Double::parseDouble(parameters.get(0))
command = getZoneCoordinates.apply(zone)
} catch (Throwable t) {
// This is multi zone
parameters.forEach[string z|
zone = transform("MAP", "Rocky_Etage.map.map", z)
command = command + getZoneCoordinates.apply(zone) + ","
]
// Remove last ','
command = command.substring(0, command.length - 1)
}
command = String::format("[%s]", command)
logInfo(logName, "Clean {} zone coordinates {}", receivedCommand.toString, command)
Rocky_ExecuteCommand.sendCommand('app_zoned_clean' + command)
end
// The sinlge switches are nice if you use e.g. Alexa. In the UI, it is nicer to have one string with command options
// This rlule will get the correct switch from the string command
rule "Saugbefehl generieren aus String"
when
Item Rocky_Saugbefehle received command
then
val ruleLogName = "Saugbefehl generieren aus String (" + receivedCommand + ") "
if (loggingInfo == true ) logInfo(logName, ruleLogName + "suche HH_" + receivedCommand + "*")
val SaugbefehlItem = gSaugSwitches.members.filter[s| s.name.startsWith("HH_" + receivedCommand)].head as SwitchItem
if (loggingInfo == true ) logInfo(logName, ruleLogName + "Item gefunden: " + SaugbefehlItem.name)
SaugbefehlItem.sendCommand(ON)
end