Roomba 980 Scripts
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.
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
Re: Roomba 980 Scripts
I've made some progress and I can authenticate locally to the Roomba with this code:
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.
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))Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts
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
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.
And the log:
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))
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
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?
Re: Roomba 980 Scripts
Yes.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?
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.
http://forums.indigodomo.com/viewtopic. ... 07&t=17302
Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts
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.
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
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
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
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
- FlyingDiver
- Posts: 7830
- Joined: Sat Jun 07, 2014 10:36 am
- 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.
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
my plugins: http://forums.indigodomo.com/viewforum.php?f=177
Re: Roomba 980 Scripts
That would be awesome!FlyingDiver wrote:If it's OK with you, I'll roll these scripts into an actual plugin.
Insteon / Z-Wave / Bryant Evolution Connex /Tesla / Roomba / Elk M1 / SiteSage / Enphase Enlighten / NOAA Alerts
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.
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
Re: Roomba 980 Scripts
It was a bit easier than I thought to get the credentials. See the following message.FlyingDiver wrote:If it's OK with you, I'll roll these scripts into an actual plugin.
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
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.
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