Roomba 980 Wifi Connectivity Reverse engineering

@kaznad,
its weird, the URL is ok.
Are you using http POST method? and you sent all the required http headers? including Authorization header with correct value format? Basic user:password where only user:password is base64 encoded and the word user: is literal, not the robotId. And the password is your password.
I get a File /umi not_found response when i use http GET method too. So try POST and send all the headers. I think the robot is checking all the correct headers to work fine, not just the Authorization header.

let me know if its works!

regards,

Hi
I’m totally new to npm and node.js so the most likely thing is that I’m doing something wrong. I installed node.js and the dorita980 package. I’m pretty sure that I know the Roomba’s IP address (it’s the one that turns up when I turn on the Roomba and open up the Android app). When I run getpassword I get ECONNREFUSED.

Here’s the output:

D:\Program Files\Platforms\Node.js\Packages\dorita980-master>npm run getpassword 192.168.000.026

dorita980@2.3.3 getpassword D:\Program Files\Platforms\Node.js\Packages\dorita980-master
node ./bin/getpassword.js “192.168.000.026”

Make sure your robot is on the Home Base and powered on (green lights on). Then press and hold the HOME button on your robot until
it plays a series of tones (about 2 seconds). Release the button and your robot will flash WIFI light. Then wait and look here…

Fatal error connecting to robot. Please verify the IP address and connectivity: { [Error: connect ECONNREFUSED 192.168.0.22:443]
code: ‘ECONNREFUSED’,
errno: ‘ECONNREFUSED’,
syscall: ‘connect’,
address: ‘192.168.0.22’,
port: 443 }

npm ERR! Windows_NT 5.1.2600
npm ERR! argv “D:\Program Files\Platforms\Node.js\node.exe” “D:\Program Files\Platforms\Node.js\node_modules\npm\bin\np
m-cli.js” “run” “getpassword” “192.168.000.026”
npm ERR! node v4.6.0
npm ERR! npm v2.15.9
npm ERR! code ELIFECYCLE
npm ERR! dorita980@2.3.3 getpassword: node ./bin/getpassword.js "192.168.000.026"
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the dorita980@2.3.3 getpassword script ‘node ./bin/getpassword.js “192.168.000.026”’.
npm ERR! This is most likely a problem with the dorita980 package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node ./bin/getpassword.js “192.168.000.026”
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs dorita980
npm ERR! Or if that isn’t available, you can get their info via:
npm ERR!
npm ERR! npm owner ls dorita980
npm ERR! There is likely additional logging output above.

I also have the npm-debug.log in case it helps. Any idea what the cause might be?

I noticed that the error output mentions a different IP address. I tried other addresses but always with the same result.

Any help is appreciated.

try npm run getpassword 192.168.0.26

what is the expected output for running https://ip:443/umi with all the correct headers?

Thanks for the suggestion. I tried it, and the result is the same (except that the error output now mentions the same IP.

node ./bin/getpassword.js "192.168.0.26"

Make sure your robot is on the Home Base and powered on (green lights on). Then press and hold the HOME button on your robot until
 it plays a series of tones (about 2 seconds). Release the button and your robot will flash WIFI light. Then wait and look here...

Fatal error connecting to robot. Please verify the IP address and connectivity: { [Error: connect ECONNREFUSED 192.168.0.26:443]
  code: 'ECONNREFUSED',
  errno: 'ECONNREFUSED',
  syscall: 'connect',
  address: '192.168.0.26',
  port: 443 }

npm ERR! Windows_NT 5.1.2600
npm ERR! argv "D:\\Program Files\\Platforms\\Node.js\\node.exe" "D:\\Program Files\\Platforms\\Node.js\\node_modules\\npm\\bin\\np
m-cli.js" "run" "getpassword" "192.168.0.26"
npm ERR! node v4.6.0
npm ERR! npm  v2.15.9
npm ERR! code ELIFECYCLE
npm ERR! dorita980@2.3.3 getpassword: `node ./bin/getpassword.js "192.168.0.26"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the dorita980@2.3.3 getpassword script 'node ./bin/getpassword.js "192.168.0.26"'.

Some output from the npm-debug.log:

9 verbose stack Error: dorita980@2.3.3 getpassword: `node ./bin/getpassword.js "192.168.0.26"`
9 verbose stack Exit status 1
9 verbose stack     at EventEmitter.<anonymous> (D:\Program Files\Platforms\Node.js\node_modules\npm\lib\utils\lifecycle.js:217:16)
9 verbose stack     at emitTwo (events.js:87:13)
9 verbose stack     at EventEmitter.emit (events.js:172:7)
9 verbose stack     at ChildProcess.<anonymous> (D:\Program Files\Platforms\Node.js\node_modules\npm\lib\utils\spawn.js:24:14)
9 verbose stack     at emitTwo (events.js:87:13)
9 verbose stack     at ChildProcess.emit (events.js:172:7)
9 verbose stack     at maybeClose (internal/child_process.js:829:16)
9 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)
10 verbose pkgid dorita980@2.3.3
11 verbose cwd D:\Program Files\Platforms\Node.js\Packages\dorita980-master
12 error Windows_NT 5.1.2600
13 error argv "D:\\Program Files\\Platforms\\Node.js\\node.exe" "D:\\Program Files\\Platforms\\Node.js\\node_modules\\npm\\bin\\npm-cli.js" "run" "getpassword" "192.168.0.26"
14 error node v4.6.0
15 error npm  v2.15.9
16 error code ELIFECYCLE
17 error dorita980@2.3.3 getpassword: `node ./bin/getpassword.js "192.168.0.26"`

i used the 000.026 method and I got the same error as you. So i figured that was the issue. You are running it on Windows right? Maybe node was not installed properly or something.

@facu how would I log the requestOptions variable in console? I tried logging it in apicall with console.log (requestOptions) but it shows as undefined. I dunno how to code in nodejs. Thanks.

Hi @LordLiverpool,
Im pretty sure that is a network issue. Are your computer in the same network? Are you sure that is the IP?
A firewall in the middle? Why that uggly IP format? :stuck_out_tongue:
Some times (after calling via HTTP GET with the browser or something without the correct headers) the robot stop responding, maybe as a protection method. When this happen, i restart my router and works again.
Reset your router and Try to get your robot IP with the getRobotIP() method:

var dorita980 = require('dorita980');

dorita980.getRobotIP(function (ierr, ip) {
  console.log(ierr);
  console.log(ip);
});

I tried dorita980 on windows with node 4.6.0 and its works fine. So check your connectivity.

regards,

Hell Boreddead and thanks a lot for the great work here! Very useful. I got my roomba to be run from Alexa via smartthings for a while but now all of a sudden I get an error message from my virtual device, knowing that I’ve checked that ID and password are ok.

Iget this message : “something went wrong: groovyx.net.http.HttpResponseException: Request missing asset ID”

Here is the code for my device handler:

metadata {
    definition (name: "ROOMBA switch", namespace: "Elfege", author: "Elfege") {
        capability "Switch"
        capability "Refresh"     

        command "dock"
        command "resume"

    }
}

// simulator metadata
simulator {
}

// UI tile definitions
tiles {
    standardTile("button", "device.switch", width: 1, height: 1, canChangeIcon: false) {
        state "off", label: 'Clean', action: "switch.on", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff", nextState: "on"
        state "on", label: 'Stop', action: "switch.off", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "off"
    }
    standardTile("button2", "device.switch", width: 1, height: 1, canChangeIcon: false) {
        state "on", label: 'Pause', action: "switch.off", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff",  nextState: "resume"
        state "resume", label: 'Resume', action: "resume", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "resuming"
        state "resuming", label: 'resuming', action: "", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "on"
    }
    standardTile("button3", "device.switch", width: 1, height: 1, canChangeIcon: false) {
        state "on", label: 'Dock', action: "dock", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff", nextState: "docking" 
        state "docking", label: 'Docking', action: "", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff", nextState: "on"
    }
    standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
        state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
    }        
    main "button"
    details(["button","button2","button3", "refresh"])
}

def parse(String description) {
}

def dock() {
    sendEvent(name: "switch", value: "on")
    log.debug "Roomba Switch is --------------------------docking"
    state.switch  = "dock"
    log.debug "Running Roomba"

    def params = [
        uri: "https://irobot.axeda.com/services/v1/rest/Scripto/execute/AspenApiRequest?blid=xxxxxxxxxxxxxxxxx&robotpwd=xxxxxxxxxxxxxxxxx&method=multipleFieldSet&value=%7B%0A%20%20%22remoteCommand%22%20:%20%22dock%22%0A%7D",

    ]

    try {
        httpGet(params) { resp ->
            resp.headers.each {
                log.debug "${it.name} : ${it.value}"
            }
            log.debug "response contentType: ${resp.contentType}"
            log.debug "response data: ${resp.data}"
        }
    } catch (e) {
        log.error "something went wrong: $e"
    }
}

def on() {
    sendEvent(name: "switch", value: "cleaning")
    log.debug "Roomba Switch is --------------------------on"
    state.switch  = "on"
    log.debug "Running Roomba"

    def params = [
        uri: "https://irobot.axeda.com/services/v1/rest/Scripto/execute/AspenApiRequest?blid=xxxxxxxxxxxxxxxxx&robotpwd=xxxxxxxxxxxxxxxxx&method=multipleFieldSet&value=%7B%0A%20%20%22remoteCommand%22%20:%20%22start%22%0A%7D",
    ]

    try {
        httpGet(params) { resp ->
            resp.headers.each {
                log.debug "${it.name} : ${it.value}"
            }
            log.debug "response contentType: ${resp.contentType}"
            log.debug "response data: ${resp.data}"
        }
    } catch (e) {
        log.error "something went wrong: $e"
    }
}

def resume() {
    sendEvent(name: "switch", value: "resume")
    log.debug "Roomba Switch is --------------------------resume"
    state.switch  = "off"
    log.debug "Running Roomba"


    def params = [
        uri: "https://irobot.axeda.com/services/v1/rest/Scripto/execute/AspenApiRequest?blid=xxxxxxxxxxxxxxxxx&robotpwd=xxxxxxxxxxxxxxxxx&method=multipleFieldSet&value=%7B%0A%20%20%22remoteCommand%22%20:%20%22resume%22%0A%7D",

    ]

    try {
        httpGet(params) { resp ->
            resp.headers.each {
                log.debug "${it.name} : ${it.value}"
            }
            log.debug "response contentType: ${resp.contentType}"
            log.debug "response data: ${resp.data}"
        }
    } catch (e) {
        log.error "something went wrong: $e"
    }
}

def off() {
    sendEvent(name: "switch", value: "off")
    log.debug "Roomba Switch is --------------------------on"
    state.switch  = "on"
    log.debug "Running Roomba"


    def params = [
        uri: "https://irobot.axeda.com/services/v1/rest/Scripto/execute/AspenApiRequest?blid=xxxxxxxxxxxxxxxxx&robotpwd=xxxxxxxxxxxxxxxxx&method=multipleFieldSet&value=%7B%0A%20%20%22remoteCommand%22%20:%20%22stop%22%0A%7D",

    ]

    try {
        httpGet(params) { resp ->
            resp.headers.each {
                log.debug "${it.name} : ${it.value}"
            }
            log.debug "response contentType: ${resp.contentType}"
            log.debug "response data: ${resp.data}"
        }
    } catch (e) {
        log.error "something went wrong: $e"
    }
}

def pause() {
    sendEvent(name: "switch", value: "off")
    log.debug "Roomba Switch is --------------------------on"
    state.switch  = "on"
    log.debug "Running Roomba"


    def params = [
        uri: "https://irobot.axeda.com/services/v1/rest/Scripto/execute/AspenApiRequest?blid=xxxxxxxxxxxxxxxxx&robotpwd=xxxxxxxxxxxxxx&method=multipleFieldSet&value=%7B%0A%20%20%22remoteCommand%22%20:%20%22pause%22%0A%7D",

    ]

    try {
        httpGet(params) { resp ->
            resp.headers.each {
                log.debug "${it.name} : ${it.value}"
            }
            log.debug "response contentType: ${resp.contentType}"
            log.debug "response data: ${resp.data}"
        }
    } catch (e) {
        log.error "something went wrong: $e"
    }
}
1 Like

Hi Elfege,

Could you give me some directions how to integrate Roomba with Smartthings and Alexa?

Really appreciate for your help. Thanks a lot.

Regards,

Ed

Hi,

In fact, I obtained all the information I needed step by step from this discussion thread. Take note of all the instructions provided by boreddead. The key is to retrieve the password and ID of your Roomba by utilizing Charles proxy (there is a free version that functions for 30 minutes, which should be sufficient). You will need to configure your phone’s Wi-Fi settings to use the proxy server, which corresponds to the IP address of the computer running Charles proxy, along with port 8888. Ensure that you accept the certificate on your phone when prompted (or in your general settings). Afterward, launch the Roomba app and examine its debug data displayed in the Charles proxy application on your computer.

Once you have completed these steps, simply search for the term “blid” to obtain the password, ID, and the complete HTTP command. With this information, you can utilize the device handler code provided above and insert your ID and password into it to create a virtual device. Just replace the fields marked “xxxxxxxxxxxxx” with the corresponding ID and password of your Roomba.

Now, I’m facing an issue where it initially worked but then stopped functioning, presumably due to SmartThings tightening their security protocols. I’m curious to know if someone can make it work again. Please inform me of any progress.

Elfège.

1 Like

Hi Elfege,

I am also trying to use your device handler with my roomba 980 and I am getting the same error:
something went wrong: groovyx.net.http.HttpResponseException: Request missing asset ID

If someone can help… Thanks :smiley:

I found what the ASSET-ID is in the debug. It’s ElPaso@irobot![16digits_deviceID].

I can’t find out where I am to implement it in the url, or even if it’s supposed to be in the url. If you have time to mess around with it and find out, let me know. 2 brains work better than 1! :slight_smile:

Thanks

here is the post from the proxy’s server :

POST https://irobot.axeda.com:443/services/v1/rest/Scripto/execute/AspenApiRequest HTTP/1.1
Host: irobot.axeda.com:443
Content-Type: application/x-www-form-urlencoded
Cookie: prd002.irobot-prd-47873-sg-prd002.irobot-prd-agents=EACJDCAKJCBP
User-Agent: aspen/1.8.0.134.1 CFNetwork/808.0.2 Darwin/16.0.0
Connection: keep-alive
Accept: /
Accept-Language: en-us
Content-Length: 69
Accept-Encoding: gzip, deflate
ASSET-ID: ElPaso@irobot!xxxxxxxxxxxxxxxx

blid=xxxxxxxxxxxxxxxx&robotpwd=xxxxxxxxxxxxxxxx&method=missionHistory

So it seems that the issue is coming from irobot they may have changed their protocol…

Using dorita code it should be possible to have it work with smartthings

Please be aware that specifying an IP address such as “192.168.000.026” may result in that last octet being interpreted as octal. For giggles try “192.168.000.032” which would translate as “192.168.0.26” when the last two octets are converted to decimal.

Some people are a real fan of the leading zero formatting, but it is a total train wreck since it means different things depending on what’s processing the data.

I mostly stopped by to say thanks for dorita980. I’ve been wanting to monitor for errors more proactively, also maybe integrating it with the lighting and security controls to run when we’re gone, and this looks like it’ll be totally cool.

2 Likes

Hi there everyone. Sorry for the delay in replying, I’ve had no time.
The network is the same. I’m pretty sure it’s the IP, because it’s the one that appears when I turn the Roomba charger on, and disappears when I turn it off. I tried all the others, with the same result. There’s only a firewall in the router for inbound traffic from the internet. The ugly IP format is because I copied and pasted from the router admin screen, hehe.

I’ll try the getRobotIP thing and see what happens.

I tried running the code and got this:

Looking for robots...
dgram.js:274
    throw new RangeError('Offset + length beyond buffer length');
    ^

RangeError: Offset + length beyond buffer length
    at Socket.send (dgram.js:274:11)
    at Socket.<anonymous> (D:\Program Files\Platforms\Node.js\node_modules\dorita980\lib\discover
.js:31:12)
    at Socket.g (events.js:260:16)
    at emitNone (events.js:72:20)
    at Socket.emit (events.js:166:7)
    at startListening (dgram.js:121:10)
    at dgram.js:221:7
    at nextTickCallbackWith3Args (node.js:453:9)
    at process._tickCallback (node.js:359:17)
    at Function.Module.runMain (module.js:443:11)
    at startup (node.js:139:18)
    at node.js:974:3

I’m sure I’m doing something really stupid.