Roomba 980 Scripts

Posted on
Fri Nov 25, 2016 10:37 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Roomba 980 Scripts

My Black Friday find was a Roomba 980. It's been years since I last had one and I'm impressed by how well this one works! The optical sensor lets it get to any room in the house and in a few cycles, it can clean everything without a lot of repetition.

I have found that it's WiFi connected and someone has hacked a javascript library to control it. https://github.com/koalazak/dorita980

I'm sure in a couple of days, I can cobble together a couple of scripts to have Roomba start cleaning when we leave the house and stop when we get home. But I thought I'd ask if anyone else has given this a shot so I don't have to recreate something that already exists.

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Sat Nov 26, 2016 6:38 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

I've made some progress and I can authenticate locally to the Roomba with this code:
Code: Select all
import requests
from base64 import b64encode

from requests.auth import HTTPBasicAuth
from requests.utils import quote

roombaIP = (indigo.variables['roombaIP'].value)
roombaPassword = (indigo.variables['roombaPassword'].value)
roombaBlid = (indigo.variables['roombaUsernameBlid'].value)

passEncode = b64encode('user:'+roombaPassword)

post_url = 'https://' + roombaIP + ':443/umi'
#post_url = 'https://irobot.axeda.com:443/services/v1/rest/Scripto/execute/AspenApiRequest'

head = {
'User-Agent': 'aspen%20production/2618 CFNetwork/758.3.15 Darwin/15.4.0',
'Authorization': 'Basic ' + passEncode,
'ASSET_ID': 'ElPaso@irobot!' + roombaBlid
}

payload = {
'blid': roombaBlid,
'robotpwd': roombaPassword,
'do': 'get',
#'method': 'multipleFieldSet&value=%7B%0A%20%20%22remoteCommand%22%20:%20%22$' + 'getStatus' + '%22%0A%7D',
'args': ['time'],
'id': '101'
}

r = requests.post(post_url, data=payload, headers=head, verify=False)

indigo.server.log(str(r))
indigo.server.log(str(r.status_code))
indigo.server.log(str(r.headers))
indigo.server.log(str(r.raw))
indigo.server.log(str(r.text))


Unfortunately, I haven't figured out how to parse the payload. If any of you are json-adept and can decode what the original author did, that would be super helpful to me.

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Sat Nov 26, 2016 6:44 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

Here's the relevant section from the original code:
Code: Select all
function apiCall (method, command, args) {
+    rid++;
+    if (rid > 1000) rid = 1;
+
+    requestOptions.body = '{"do":"' + method + '","args":["' + command + '"' + (args ? ', ' + JSON.stringify(args) : '') + '],"id":' + rid + '}';
+
+    return request(requestOptions).then(JSON.parse);
+  }
+
+  return {
+    getTime: () => apiCall('get', 'time'),
+    getBbrun: () => apiCall('get', 'bbrun'),
...
+    start: () => apiCall('set', 'cmd', {'op': 'start'}),
+    pause: () => apiCall('set', 'cmd', {'op': 'pause'}),
+    stop: () => apiCall('set', 'cmd', {'op': 'stop'}),
+    resume: () => apiCall('set', 'cmd', {'op': 'resume'}),
+    dock: () => apiCall('set', 'cmd', {'op': 'dock'}),


Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Sun Nov 27, 2016 10:40 am
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

I know more about javascript than I ever wanted to know.

Success. This code gets the time from the Roomba. A bit more digging and I'll have the other commands I care about.
Code: Select all
import requests
import json
from base64 import b64encode

from requests.auth import HTTPBasicAuth
from requests.utils import quote

roombaIP = (indigo.variables['roombaIP'].value)
roombaPassword = (indigo.variables['roombaPassword'].value)
roombaBlid = (indigo.variables['roombaUsernameBlid'].value)

passEncode = b64encode('user:'+roombaPassword)

post_url = 'https://' + roombaIP + ':443/umi'

head = {
'Authorization': 'Basic ' + passEncode
}

payload = {
"do": "get",
"args": ["time"],
"id": "1"
}

r = requests.post(post_url, json=payload, headers=head, verify=False)

broken = str(r.text)
fixed = broken.replace('"''}','')

data = json.loads(fixed)

indigo.variable.updateValue('roombaD', value=str(data['ok']['d']))
if data['ok']['m'] < 10:
   indigo.variable.updateValue('roombaTime', value=(str(data['ok']['h']) + ':0' + str(data['ok']['m'])))
else:
   indigo.variable.updateValue('roombaTime', value=(str(data['ok']['h']) + ':' + str(data['ok']['m'])))

indigo.server.log(str(r.raw))
indigo.server.log(str(r.text))


And the log:
Code: Select all
   Script                          {'do': 'get', 'args': ['time'], 'id': '1'}
   Script                          <Response [200]>
   Script                          https://10.0.1.91:443/umi
   Script                          <bound method Response.json of <Response [200]>>
   Script                          200
   Script                          {'Transfer-Encoding': 'chunked', 'Connection': 'close', 'Content-Type': 'application/json', 'Server': 'Marvell-WM'}
   Script                          <requests.packages.urllib3.response.HTTPResponse object at 0x110d28250>
   Script                          {"ok":{"d":"sun","h":10,"m":7},"id":1"}}
Last edited by Bollar on Mon Nov 28, 2016 3:10 pm, edited 1 time in total.

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Sun Nov 27, 2016 8:08 pm
loafbread offline
Posts: 137
Joined: May 25, 2009

Re: Roomba 980 Scripts

I look forward to trying this as soon as I get my blid. I assume the only way to do that is to do the 'npm run getpassword IPaddress' from the GitHub repository?

Posted on
Sun Nov 27, 2016 8:17 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

loafbread wrote:
I look forward to trying this as soon as I get my blid. I assume the only way to do that is to do the 'npm run getpassword IPaddress' from the GitHub repository?

Yes.

I could probably replicate his code, but since I only needed to do it once, my motivation was very low.

I'm still trying to sort out the malformed JSON output so I can read the robot's status.

viewtopic.php?f=107&t=17302

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Sun Nov 27, 2016 10:18 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

Here's the "Mission Status" script. It gives all of the current information from the Roomba, including the polar coordinates from the base - I'm sure someone bored enough could plug that into one of the other Indigo PlugIns to map the robot's progress.

You'll need to create all of the variables.

Code: Select all
import requests
import json
from base64 import b64encode

from requests.auth import HTTPBasicAuth
from requests.utils import quote

roombaIP = (indigo.variables['roombaIP'].value)
roombaPassword = (indigo.variables['roombaPassword'].value)
roombaBlid = (indigo.variables['roombaUsernameBlid'].value)

passEncode = b64encode('user:'+roombaPassword)

post_url = 'https://' + roombaIP + ':443/umi'

head = {
'Authorization': 'Basic ' + passEncode
}

payload = {
"do": "get",
"args": ["mssn"],
"id": "1"
}

r = requests.post(post_url, json=payload, headers=head, verify=False)

broken = str(r.text)
fixed = broken.replace('"''}','')

data = json.loads(fixed)

indigo.variable.updateValue('roombaFlags', value=str(data['ok']['flags']))
indigo.variable.updateValue('roombaCycle', value=str(data['ok']['cycle']))
indigo.variable.updateValue('roombaPhase', value=str(data['ok']['phase']))
indigo.variable.updateValue('roombaTheta', value=str(data['ok']['pos']['theta']))
indigo.variable.updateValue('roombaX', value=str(data['ok']['pos']['point']['x']))
indigo.variable.updateValue('roombaY', value=str(data['ok']['pos']['point']['y']))
indigo.variable.updateValue('roombaBatPct', value=str(data['ok']['batPct']))
indigo.variable.updateValue('roombaExpireM', value=str(data['ok']['expireM']))
indigo.variable.updateValue('roombaRechrgM', value=str(data['ok']['rechrgM']))
indigo.variable.updateValue('roombaError', value=str(data['ok']['error']))
indigo.variable.updateValue('roombaNotReady', value=str(data['ok']['notReady']))
indigo.variable.updateValue('roombaMssnM', value=str(data['ok']['mssnM']))
indigo.variable.updateValue('roombaSqFt', value=str(data['ok']['sqft']))
indigo.variable.updateValue('roombaID', value=str(data['id']))

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Sun Nov 27, 2016 10:26 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

Revised version of Get Time:
Code: Select all
import requests
import json
from base64 import b64encode

from requests.auth import HTTPBasicAuth
from requests.utils import quote

roombaIP = (indigo.variables['roombaIP'].value)
roombaPassword = (indigo.variables['roombaPassword'].value)
roombaBlid = (indigo.variables['roombaUsernameBlid'].value)

passEncode = b64encode('user:'+roombaPassword)

post_url = 'https://' + roombaIP + ':443/umi'

head = {
'Authorization': 'Basic ' + passEncode
}

payload = {
"do": "get",
"args": ["time"],
"id": "1"
}

r = requests.post(post_url, json=payload, headers=head, verify=False)

broken = str(r.text)
fixed = broken.replace('"''}','')

data = json.loads(fixed)

indigo.variable.updateValue('roombaD', value=str(data['ok']['d']))
indigo.variable.updateValue('roombaTime', value=(str(data['ok']['h']) + ':' + str(data['ok']['m'])))

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Sun Nov 27, 2016 10:50 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

The Start script - also works for dock, pause, stop & resume.
Code: Select all
import requests
import json
from base64 import b64encode

from requests.auth import HTTPBasicAuth
from requests.utils import quote

roombaIP = (indigo.variables['roombaIP'].value)
roombaPassword = (indigo.variables['roombaPassword'].value)
roombaBlid = (indigo.variables['roombaUsernameBlid'].value)

passEncode = b64encode('user:'+roombaPassword)

post_url = 'https://' + roombaIP + ':443/umi'

head = {
'Authorization': 'Basic ' + passEncode
}

payload = {
"do": "set",
# You can replace start with dock, pause, stop & resume
"args": ["cmd", {"op":"start"}],
"id": "1"
}

r = requests.post(post_url, data=json.dumps(payload), headers=head, verify=False)

broken = str(r.text)
fixed = broken.replace('"''}','')

data = json.loads(fixed).

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Mon Nov 28, 2016 3:11 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

Bollar wrote:
Revised version of Get Time:
Code: Select all
import requests
import json
from base64 import b64encode

from requests.auth import HTTPBasicAuth
from requests.utils import quote

roombaIP = (indigo.variables['roombaIP'].value)
roombaPassword = (indigo.variables['roombaPassword'].value)
roombaBlid = (indigo.variables['roombaUsernameBlid'].value)

passEncode = b64encode('user:'+roombaPassword)

post_url = 'https://' + roombaIP + ':443/umi'

head = {
'Authorization': 'Basic ' + passEncode
}

payload = {
"do": "get",
"args": ["time"],
"id": "1"
}

r = requests.post(post_url, json=payload, headers=head, verify=False)

broken = str(r.text)
fixed = broken.replace('"''}','')

data = json.loads(fixed)

indigo.variable.updateValue('roombaD', value=str(data['ok']['d']))
if data['ok']['m'] < 10:
   indigo.variable.updateValue('roombaTime', value=(str(data['ok']['h']) + ':0' + str(data['ok']['m'])))
else:
   indigo.variable.updateValue('roombaTime', value=(str(data['ok']['h']) + ':' + str(data['ok']['m'])))

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Mon Nov 28, 2016 3:49 pm
FlyingDiver offline
User avatar
Posts: 7210
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Roomba 980 Scripts

FYI - I have a Roomba 980 on order, it'll be here in a couple days.

If it's OK with you, I'll roll these scripts into an actual plugin.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Mon Nov 28, 2016 3:50 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

FlyingDiver wrote:
If it's OK with you, I'll roll these scripts into an actual plugin.

That would be awesome!

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Mon Nov 28, 2016 3:53 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

I achieved nirvana today. My better half went out during the day and while she was out, Roomba did some cleaning. I had it start a cycle a minute after she armed the security system and I had it dock as soon as Geofency reported she was near home. It completed docking about 10 seconds after she disarmed the alarm -- if I set that geofence just a bit larger, she won't even notice it ran.

Next on my list is to decode the status messages so I can report when the bin needs emptying.

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Mon Nov 28, 2016 4:33 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

FlyingDiver wrote:
If it's OK with you, I'll roll these scripts into an actual plugin.


It was a bit easier than I thought to get the credentials. See the following message.
Last edited by Bollar on Mon Nov 28, 2016 6:28 pm, edited 1 time in total.

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Posted on
Mon Nov 28, 2016 6:26 pm
Bollar offline
Posts: 528
Joined: Aug 11, 2013

Re: Roomba 980 Scripts

This script gets the credentials that allows the other scripts to run. You need to know the Roomba's IP address and create the roombaIP variable. The author of dorita980 made a big deal about making sure the headers were right, but I haven't found the Roomba to be sensitive to the headers at all.

To get Roomba into the proper mode, Roomba must be in the dock. Hold down the "Home" button until Roomba plays a four note confirmation sound. Then run this script. There's no error checking, so if you get the timing wrong, you may need to execute again. If the script fails, you'll need to put Roomba back into this mode again.

Code: Select all
import requests
import json
from base64 import b64encode

from requests.auth import HTTPBasicAuth
from requests.utils import quote

roombaIP = (indigo.variables['roombaIP'].value)
#roombaPassword = (indigo.variables['roombaPassword'].value)
#roombaBlid = (indigo.variables['roombaUsernameBlid'].value)


post_url = 'https://' + roombaIP + ':443/umi'

head = {
}

payload = {
"do": "get",
"args": ["passwd"],
"id": "1"
}

r = requests.post(post_url, json=payload, headers=head, verify=False)

broken = str(r.text)
fixed = broken.replace('"''}}','}')

data = json.loads(fixed)
pwd = str(data['ok']['passwd'])


indigo.server.log('Password: ' + pwd)

passEncode = b64encode('user:'+ pwd)


head = {
'Authorization': 'Basic ' + passEncode
}

payload = {
"do": "get",
"args": ["sys"],
"id": "1"
}

r = requests.post(post_url, json=payload, headers=head, verify=False)

broken = str(r.text)
fixed = broken.replace('"''}','')
data = json.loads(fixed)

blid = ''

for element in data['ok']['blid']:
   blidA = hex(element)
   blidB = blidA.replace('0x','')
   blidC = blidB.upper()
   blid += str(blidC)

indigo.server.log('Blid: ' + blid)

#indigo.variable.updateValue('roombaPassword', value=str(pwd))
#indigo.variable.updateValue('roombaUsernameBlid', value=str(blid))

Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts

Who is online

Users browsing this forum: No registered users and 2 guests

cron