NickWaterton / Roomba980-Python

Python program and library to control iRobot Roomba 980 Vacuum Cleaner
MIT License
386 stars 109 forks source link

Selective (Room Based) Cleaning #80

Open ncsufan8628 opened 4 years ago

ncsufan8628 commented 4 years ago

Hello,

Thanks for a great solution for integrating and controlling my Roomba i7 via Openhab.

I did have a question I could not seem to find an answer to. I have created several Smart Maps using the iRobot app on my mobile device to correspond to the different floors/rooms of my house. I am able to successfully pass basic commands to Roomba like start, dock, pause etc. however, I cannot figure out how to tell Roomba to use these maps to clean a specific area of my home.

I can see when initiating a room-specific clean that the data is showing up in the logs/statuses of this script. Any suggestions as to how I can pass these variables when initiating a cleaning job?

Received Roomba Data Mr. Sweepy: $aws/things/28A5EE62A8214B0F9096FD45C8019CB2/shadow/update, b'{"state":{"reported":{"batPct": 100, "batteryType": "F12432832R", "bbchg": {"nChatters": 3, "nKnockoffs": 17, "nLithF": 2, "nChgOk": 28, "aborts": [0, 16, 5], "smberr": 57344}, "bbchg3": {"estCap": 1875, "nAvail": 117, "hOnDock": 500, "avgMin": 113}, "bbmssn": {"aCycleM": 53, "nMssnF": 9, "nMssnC": 19, "nMssnOk": 36, "aMssnM": 83, "nMssn": 65}, "bbnav": {"aMtrack": 60, "nGoodLmrks": 0, "aGain": 15, "aExpo": 47}, "bbpause": {"pauses": [14, 2, 42, 2, 2, 42, 5, 5, 33, 18]}, "bbrstinfo": {"nNavRst": 0, "nMobRst": 0, "causes": "0000", "nSafRst": 0, "safCauses": [13, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, "bbrun": {"nEvacs": 0, "nCBump": 0, "nOvertemps": 0, "nWStll": 3, "nMBStll": 5727, "nPanics": 521, "nPicks": 254, "nScrubs": 28, "nStuck": 25, "sqft": 221, "nOpticalDD": 10, "min": 35, "nPiezoDD": 18, "hr": 50, "nCliffsF": 2682, "nCliffsR": 0}, "bbswitch": {"nBumper": 61880, "nDrops": 1061, "nDock": 73, "nSpot": 80, "nClean": 73}, "bbsys": {"min": 53, "hr": 598}, "bin": {"present": true, "full": false}, "binPause": false, "bleDevLoc": false, "cap": {"binFullDetect": 1, "dockComm": 1, "wDevLoc": 2, "bleDevLoc": 1, "maps": 3, "edge": 0, "pmaps": 2, "tLine": 2, "area": 1, "eco": 1, "multiPass": 2, "pose": 1, "team": 1, "pp": 0, "5ghz": 1, "prov": 3, "sched": 1, "svcConf": 1, "ota": 2, "log": 2}, "carpetBoost": false, "cleanMissionStatus": {"cycle": "clean", "phase": "run", "expireM": 0, "rechrgM": 0, "error": 0, "notReady": 0, "mssnM": 1, "sqft": 0, "initiator": "rmtApp", "nMssn": 66}, "cleanSchedule2": [], "cloudEnv": "prod", "connected": true, "country": "US", "deploymentState": 0, "dock": {"known": true, "pn": null, "state": 300, "id": null, "fwVer": null}, "ecoCharge": false, "hwPartsRev": {"csscID": 0, "mobBrd": 7, "mobBlid": "398BAE19F6870CA448E9F26B9929B75B", "navSerialNo": "CF0980ZY1", "wlan0HwAddr": "50:14:79:17:57:c0", "NavBrd": 0}, "langs": [{"cs-CZ": 0}, {"da-DK": 1}, {"de-DE": 2}, {"en-GB": 3}, {"en-US": 4}, {"es-ES": 5}, {"es-XL": 6}, {"fi-FI": 7}, {"fr-CA": 8}, {"fr-FR": 9}, {"he-IL": 10}, {"it-IT": 11}, {"ja-JP": 12}, {"nb-NO": 13}, {"nl-NL": 14}, {"pl-PL": 15}, {"pt-BR": 16}, {"pt-PT": 17}, {"ru-RU": 18}, {"sv-SE": 19}, {"zh-CN": 20}, {"zh-HK": 21}, {"zh-TW": 22}], "language": 4, "lastCommand": {"command": "start", "initiator": "rmtApp", "time": 1587654147, "ordered": 0, "pmap_id": "3Cpr2Uh4SpK8vcdXjjPqpg", "regions": [{"region_id": "25", "region_name": "kitchy", "region_type": "custom", "type": "rid"}], "user_pmapv_id": "200402T230528"}, "lastDisconnect": 0, "mapUploadAllowed": true, "mssnNavStats": {"nMssn": 65, "gLmk": 0, "lmk": 0, "reLc": 0, "plnErr": "none", "mTrk": 60, "kdp": 0, "sfkdp": 0, "nmc": 3, "nmmc": 0, "nrmc": 0, "mpSt": "idle", "l_drift": 0, "h_drift": 0, "l_squal": 88, "h_squal": 0}, "name": "Mr. Sweepy", "noAutoPasses": false, "noPP": false, "openOnly": false, "pmapLearningAllowed": true, "pmaps": [{"3Cpr2Uh4SpK8vcdXjjPqpg": "200402T230528"}, {"aAk2dKIUTdmxFfka9xd6OA": "200402T223453"}, {"E5y5ts-WTNmzDRTh2BGGzA": "200408T154042"}], "pose": {"theta": 89, "point": {"x": -253, "y": -372}}, "rankOverlap": 15, "sceneRecog": 1, "schedHold": false, "secureBoot": {"log": 2, "flip": 0, "sbl1Ver": "B3.2.02_PPUB", "stublVer": "B3.2.02_PPUB", "efuse": 1, "blType": 1, "enforce": 2, "lastRst": "200000001", "recov": "linux+2.0.9+lewis-ota3+21", "idSwitch": 0}, "sku": "i715020", "softwareVer": "lewis+3.2.9+lewis-release-rt419+12", "subModSwVer": {"nav": "lewis-nav+3.2.4-EPMF+build-HEAD-7834b608797+12", "mob": "3.2.4-XX+build-HEAD-7834b608797+12", "pwr": "0.5.0+build-HEAD-7834b608797+12", "sft": "1.1.0+Lewis-Builds/Lewis-Certified-Safety/lewis-safety-bbbe81f2c82+21", "mobBtl": "4.2", "linux": "linux+2.1.6_lock-1+lewis-release-rt419+12", "con": "2.1.6-tags/release-2.1.6@c6b6585a/build"}, "svcEndpoints": {"svcDeplId": "v005"}, "timezone": "America/New_York", "tls": {"tzbChk": 1, "privKType": 2, "lcCiphers": [0, 0, 0, 0, 0, 0, 0, 50380848, 50380847, 50331708]}, "twoPass": false, "tz": {"events": [{"dt": 1583082000, "off": -300}, {"dt": 1583650801, "off": -240}, {"dt": 1604210401, "off": -300}], "ver": 8}, "vacHigh": false, "wDevLoc": false}}}'<

NickWaterton commented 4 years ago

The Roomba module was written for the Roomba 980, which doesn't have selective maps, so it wasn't a feature that was included.

Having said that, I have just pushed version 1.2.9, which now allows you to send any command to the roomba using json.

Normally you just send a one word command such as:

mosquitto_pub -t /roomba/command -m "start"

Which will still work, but now you could also send this as:

mosquitto_pub -t /roomba/command -m "{\"command\": \"start\"}"

You send the command as a json string to an mqtt topic /roomba/command (whatever your roomba command topic is).

So, in your case the json command would be:

 {"command": "start", "initiator": "rmtApp", "time": 1587654147, "ordered": 0, "pmap_id": "3Cpr2Uh4SpK8vcdXjjPqpg", "regions": [{"region_id": "25", "region_name": "kitchy", "region_type": "custom", "type": "rid"}], "user_pmapv_id": "200402T230528"}

initiator, and time will get overwritten with localApp and the current timestamp, so it doesn't really matter what you put in for those fields. If you send this using mosquitto_pub you have to escape the " as in my example, but usually you are sending this from python, or OH/some other application. Sending json using mqtt from OH is a PITA, but it can be done (with a rule say).

This is obviously untested, as I don't have a roomba i7, but give it a try and see if it works.

ncsufan8628 commented 4 years ago

Hey Nick, confirmed working. Thanks for a quick and thorough response. Tried by just sending the command via MQTT, and was able to run a few successful cleans.

Will mess around more tomorrow with different rooms/room combos. Looks like I just take the command the app sends (shown in the log of Roomba980) & reissue via MQTT or openhab in order to duplicate the same functionality.

To all other i7 owners out there searching for this functionality, following the last command Nick posted, but replacing with the information that comes from the Roomba980-Python script should get you room specific cleaning jobs!

For others - To get this info for your specific map/Roomba:

  1. Run roomba.py to connect to your robot
  2. Issue a room specific cleaning job via the app
  3. Look through the script output for a section similar to the bolded one in my OP
  4. Reissue that command to /roomba/command via mqtt
  5. Win!
simontims commented 4 years ago

Great work, thanks. Will test with my i7 soon as I get a chance to update.

NickWaterton commented 4 years ago

Good to know it works,

Looking at your log output, it seems that you have three maps defined:

"pmaps": [{"3Cpr2Uh4SpK8vcdXjjPqpg": "200402T230528"}, {"aAk2dKIUTdmxFfka9xd6OA": "200402T223453"}, {"E5y5ts-WTNmzDRTh2BGGzA": "200408T154042"}]

I assume these correspond to three different areas/floors or something. The 200402T230528 looks like a timestamp (ie 2020 April 2nd at 11:05:28 pm - probably UTC, so that would be 6:05:28pm in your timezone), maybe they are using it as a version number of the map? So if you edit the map, the pmap_id would stay the same, but the user_pmapv_id would update to a new timestamp.

Within the map, there are probably sub areas (regions), corresponding to rooms, Your kitchen seems to be defined as:

{"region_id": "25", "region_name": "kitchy", "region_type": "custom", "type": "rid"}

So you can probably specify multiple regions/rooms per map by adding the region to the regions list in the start command. You'll have to figure out what the region id's are by experimenting. I don't know exactly what type means but rid looks like it stands for Region ID ie you are sending a region id (in this case 25). maybe there are other types that can be sent.

So you can probably send cleaning jobs for multiple rooms by sending something like:

{"command": "start", "initiator": "rmtApp", "time": 1587654147, "ordered": 0, "pmap_id": "3Cpr2Uh4SpK8vcdXjjPqpg", "regions": [{"region_id": "25", "region_name": "kitchy", "region_type": "custom", "type": "rid"}, {"region_id": "X", "region_name": "yyyy", "region_type": "custom", "type": "rid"}], "user_pmapv_id": "200402T230528"}

Where X is the region id number and yyyy is the name (figured out by trial and error). The region id's are probably specific to a map, so you would have to make sure they correspond.

If you edit the map, you would probably have to update the user_pmapv_id in the command. It might be worth experimenting, and seeing what the minimum command you can send and have it work is. For instance, can you leave the user_pmapv_id out and have it still work (maybe it uses the latest map by default?). That would be helpful, as you could then edit the map without having to update the command.

Don't know what ordered is, but it could be whether the regions are cleaned in order (1) or most efficient (0) - it's numeric, so you can experiment with it. Might only be relevant if you have multiple regions to clean in the list.

If someone wants to send me a Roomba i7 I can probably figure it out...

ncsufan8628 commented 4 years ago

So I did have a ton of time to play with it over the weekend. You are right Nick, once I discovered roomba's other region identifiers, inserting them was fairly easy to get room specific cleaning jobs going that either used one or some of the rooms defined.

I only changed the region info within the []. I left user_pmapv_idthe same. I noticed that when sniffing commands from the app that the value was always constant for the main floor of my home.

I still need to build out the functionality for the other two floors, I am assuming the pmap_id & user_pmapv_id will change then.

I haven't played around with ordered yet. Need to finish basic room-based cleaning build out before the wife kills me for monopolizing her birthday gift!

NickWaterton commented 4 years ago

I understand, I just ordered a weather station for my wife that she doesn't know she needs yet. It'll help with her gardening!

I feel that I should point out that traditionally women are allowed to divorce you if you buy them a vacuum cleaner for their birthday. Just saying...

NickWaterton commented 3 years ago

I now have an s9 and a Braava jet m6.

I have re-written the library, and will be adding selective mapping. Anything else you found out about it would be appreciated, or I will have to figure it out myself.

New version should be up in a few days.

ncsufan8628 commented 3 years ago

Hey Nick,

here is an example of my full command I use to issue selective cleaning commands to Roomba. Keep in mind, I had to use the app to fully setup and run the training runs before using this.

(mosquitto_pub -t /roomba/command -u USERNAME -P PASSWORD -m "{\"command\": \"start\", \"ordered\": 0, \"pmap_id\": \"ROOM ID GOTTEN FROM MQTT SPY\", \"regions\": [{\"region_id\": \"REGION ID FROM MQTT SPY\", \"region_name\": \"ROOM NAME YOU GAVE IN THE APP\", \"region_type\": \"REGION TYPE YOU SELECTED IN THE APP\", \"type\": \"rid\"}" && echo "Success")

I found these commands by running MQTT spy and looking at the last command that was issued to Roomba when running a selective clean for each specific room (using the app). This is the specific piece you are looking for & IIRC the easiest way I was able to get this was by running cleaning missions for each room I had setup via the app and then checking Roomba980 Python for what was sent to the roomba via Last Command:

{\"command\": \"start\", \"ordered\": 0, \"pmap_id\": \"ROOM ID GOTTEN FROM MQTT SPY\", \"regions\": [{\"region_id\": \"REGION ID FROM MQTT SPY\", \"region_name\": \"ROOM NAME YOU GAVE IN THE APP\", \"region_type\": \"REGION TYPE YOU SELECTED IN THE APP\", \"type\": \"rid\"}

From there, I wrote a [shitty and clunky] script which I use with the Exec binding in OH to trigger the appropriate combo of commands depending on the rooms I want cleaned.

Hope thats helpful!

NickWaterton commented 3 years ago

Thanks,

I’m working on the new version...

Hudrolax commented 2 years ago

Hello from Russia. Thanks for great work. I have been tied use API to control my Roomba i7+. I run roomba.py with -wp 8200 and try to use HTTP API to control my i7+, but it works only with simple commands like http://localhost:8200/api/local/action/start and only with GET. POST API doesn't work, because the error in <def post_items(self, setting, value='')> function as I understood (got 400 error always). Then I wrote new function for sending commands without checking command name and replacing any parameters in JSON. I caught the last command by lastCommand GET API and repeated it to start cleaning with a specific area (region), but robot just have no reaction. Then I tried using Mosquitto because I think that HTTP API doesn't work right, but I have never used it before. I tried run Mosquitto_sub and Mosquitto_pub in a different terminals and send commands. It's worked. Then I ran roomba.py in Ubuntu and tried to send <mosquitto_pub -t /roomba/command -m "start"> command from the same machine terminal and I got nothing. Robot and roomba.py console just doesn't react. May be Mosquitto server doesn't work in roomba.py? How can I start Mosquitto server for sending commands like <mosquitto_pub -t /roomba/command -m "start">? It starts by default or do I have to run roomba.py with specific keys? In this topic guy used Mosquitto for send specific region for cleaning.

ncsufan8628 commented 2 years ago

You need to setup and configure roomba.py as a linux service and configure it to speak to your MQTT server. If roomba.py is not running and not communicating with your MQTT server, then your Mosquitto_pub commands wont do anything.

Download the program MQTT Spy and make sure your roomba is connected to the MQTT server and reporting its status.

NickWaterton commented 2 years ago

@Hudrolax

I suggest you read through the documentation, it covers the usage with mqtt quite thoroughly. Mqtt works better than the REST interface. You need to run roomba.py with some command line options, or edit the config.ini file to include the mqtt broker IP address, username and login (these are optional).

What machine is your Mosquitto broker running on? If it’s a different machine than the one you are issuing command on, you have to add -h broker-ip-address to the mosquitto_pub commands. If the broker is on the same machine, you can leave it out.

Everything works on Ubuntu, that’s what I run roomba.py on myself, and I also use mosquitto running on Ubuntu as well.

Hudrolax commented 2 years ago

@NickWaterton roomba.py is run broker by self? Or broker is it different application that i install and run by myself? I install mosquitto by and it's working when i check it through 2 terminals with mosquitto_sub and mosquitto_pub commands. But when i run roomba.py on the same machine and send <mosquitto_pub -t /roomba/command -m "start"> from the same machine i got nothing (roomba.py runned without any arguments). mosquitto_pub not get any errors and roomba.py console don't show any logs about command.

Hudrolax commented 2 years ago

Sorry for the stupid questions, but i am a novice in mosquito technology. I installed MQTT Explorer and it's helping me to understand how it works. I append the name of my Roomba's robot to the topic address and its work! <mosquitto_pub -t /roomba/command/Bender -m "start">

Hudrolax commented 2 years ago

It works! This command sended by MQTT to topic /roomba/command/Bender works (Bender is my Roomba's robot name). Robot go to region 2 and starts cleaning.


{
  "command": "start",
  "ordered": 0,
  "pmap_id": "rWTTOaqjRpa5KaNnPB3CaA",
  "regions": [
    {
      "region_id": "2",
      "type": "zid"
    }
  ]
}
NickWaterton commented 2 years ago

Glad you figured it out!

yuguar commented 2 years ago

Hey Nick,

here is an example of my full command I use to issue selective cleaning commands to Roomba. Keep in mind, I had to use the app to fully setup and run the training runs before using this.

(mosquitto_pub -t /roomba/command -u USERNAME -P PASSWORD -m "{"command": "start", "ordered": 0, "pmap_id": "ROOM ID GOTTEN FROM MQTT SPY", "regions": [{"region_id": "REGION ID FROM MQTT SPY", "region_name": "ROOM NAME YOU GAVE IN THE APP", "region_type": "REGION TYPE YOU SELECTED IN THE APP", "type": "rid"}" && echo "Success")

I found these commands by running MQTT spy and looking at the last command that was issued to Roomba when running a selective clean for each specific room (using the app). This is the specific piece you are looking for & IIRC the easiest way I was able to get this was by running cleaning missions for each room I had setup via the app and then checking Roomba980 Python for what was sent to the roomba via Last Command:

{"command": "start", "ordered": 0, "pmap_id": "ROOM ID GOTTEN FROM MQTT SPY", "regions": [{"region_id": "REGION ID FROM MQTT SPY", "region_name": "ROOM NAME YOU GAVE IN THE APP", "region_type": "REGION TYPE YOU SELECTED IN THE APP", "type": "rid"}

From there, I wrote a [shitty and clunky] script which I use with the Exec binding in OH to trigger the appropriate combo of commands depending on the rooms I want cleaned.

Hope thats helpful!

Hi , Sorry for question not about this repository but , ncsufan8628 can you please elaborate more on that script of yours and exec binding for OH3 . I'm using OH3's official binding and having trouble with specific rooms commands . Thanks .