iRobot 9xx on openHAB

Hello everybody,

based on koalazak and NickWaterton work i figured out how openHAB can natively speak with Roomba via MQTT2 binding. May be it’s of interest.

First the hard part of the job or how the things must be defined:

Bridge mqtt:broker:roomba "Roomba" @ "Cleaner" [ clientID="<blid>", host="<ip>", port=8883, secure=true,
                                                 username="<blid>", password="<pwd>",
                                                 certificatepin=true, publickeypin=true ]
{
  Thing topic state "Roomba state" @ "Cleaner" {
    Channels:
      Type string : wifistat "WiFi"    [ stateTopic="wifistat" ]
      Type string : shadow   "Status"  [ stateTopic="$aws/things/<blid>/shadow/#" ]
  }
}

Now the things are easy:

String RoombaWifistatJSON "Roomba WiFi [%s]"   { channel="mqtt:topic:roomba:state:wifistat" }
String RoombaShadowJSON   "Roomba State [%s]" { channel="mqtt:topic:roomba:state:shadow" }

and we have JSON reply from Roomba broker. There are a lot of messages Roomba send back. Therefore i wrote some rules to get the stuff sorted:

rule "RoombaWifistatJSONReceivedUpdate"
when
  Item RoombaWifistatJSON received update
then
  val String json = RoombaWifistatJSON.state.toString
  if(json !== null) {
    for(String key: transform("JS", "roomba.js", json).split(",")) {
      if(key == "localtimeoffset") {
      } else if(key == "mac") {
      } else if(key == "netinfo") {
      } else if(key == "signal") {
      } else if(key == "pose") {
      } else if(key == "utctime") {
      } else if(key == "wifistat") {
      } else if(key == "wlcfg") {
      } else {
        logInfo("RoombaWifistatJSON", "JSON: " + json + " Key: " + key)
      }
    }
  }
end

rule "RoombaShadowJSONReceivedUpdate"
when
  Item RoombaShadowJSON received update
then
  val String json = RoombaShadowJSON.state.toString
  if(json !== null) {
    for(String key: transform("JS", "roomba.js", json).split(",")) {
      if(key == "audio") {
      } else if(key == "batPct") {
      } else if(key == "batteryType") {
      } else if(key == "bbchg") {
      } else if(key == "bbmssn") {
      } else if(key == "bbrstinfo") {
      } else if(key == "bbchg3") {
      } else if(key == "bbnav") {
      } else if(key == "bbpanic") {
      } else if(key == "bbpause") {
      } else if(key == "bbrun") {
      } else if(key == "bbswitch") {
      } else if(key == "bbsys") {
      } else if(key == "bin") {
      } else if(key == "binPause") {
      } else if(key == "bootloaderVer") {
      } else if(key == "cap") {
      } else if(key == "carpetBoost") {
      } else if(key == "cleanMissionStatus") {
      } else if(key == "cleanSchedule") {
      } else if(key == "cloudEnv") {
      } else if(key == "country") {
      } else if(key == "connected") {
      } else if(key == "dock") {
      } else if(key == "ecoCharge") {
      } else if(key == "hardwareRev") {
      } else if(key == "langs") {
      } else if(key == "language") {
      } else if(key == "lastCommand") {
      } else if(key == "name") {
      } else if(key == "navSwVer") {
      } else if(key == "noAutoPasses") {
      } else if(key == "noPP") {
      } else if(key == "mapUploadAllowed") {
      } else if(key == "mobilityVer") {
      } else if(key == "openOnly") {
      } else if(key == "schedHold") {
      } else if(key == "sku") {
      } else if(key == "softwareVer") {
      } else if(key == "soundVer") {
      } else if(key == "svcEndpoints") {
      } else if(key == "timezone") {
      } else if(key == "twoPass") {
      } else if(key == "tz") {
      } else if(key == "uiSwVer") {
      } else if(key == "umiVer") {
      } else if(key == "vacHigh") {
      } else if(key == "wifiSwVer") {
      } else {
        logInfo("RoombaShadowJSON", "JSON: " + json + " Key: " + key)
      }
    }
  }
end

And as last roomba.js script used as transform:

(function(json){
  var result = "";
  try {
    var buffer = JSON.parse(json).state.reported;
    result = Object.keys(buffer).toString()
  }
  catch(error) {
    result = error;
  }
  finally {
    return result;
  }
})(input)

Sending of commands is done via

val RoombaActions = getActions("mqtt", "mqtt:broker:roomba")

// Commands: "start", "stop", "pause", "resume", "dock"
val GetCommandJSON = [ String command |
  String::format("{\"command\":\"%s\",\"time\":%d,\"initiator\":\"localApp\"}", command, now.millis / 1000)
]

// Settings; Send on "delta",
// carpetBoost true/false, vacHigh true/false, binPause true
//           openOnly true this is edge clean - set to false to enable edge cleaning
//           noAutoPasses true/false twoPass true/false
val GetSettingJSON = [ String command |
  String::format("{\"state\":\"%s\"}")
]

For example to send Roomba home, follow rule can be used:

rule "RoombaDockReceivedCommand"
when
  Item RoombaDock received command ON
then
  RoombaActions.publishMQTT("cmd", GetCommandJSON.apply("dock"))
end

mainly that’s all. To draw a map, i use currently influxdb/grafana.

Kind regards,

Alexander

7 Likes