Roomba 980 Wifi Connectivity Reverse engineering

Hi @Elfege_Leylavergne,
First: The ASSET-ID is a http header that axeda cloud needs to work now with your request. Try to send the header with the correct value (ElPaso@irobot!xxxxx where xxxx is the robot blid) in your request. (i dont kwno how to add headers with your python request module, i thing is inside params object).
@vwatson FYI.

Second: dorita980 is useful to control your robot from node.js projects. I use it on a HomeKit Accessory Server (like HAP-NodeJS) to control the robot with Siri for example.
Smartthings is also extending his plugin system to support node.js connectos instead of using python.
Also dorita980 is useful to get your robot password without charles proxy and that MITM atack. And with dorita980 you get your robot IP, for example.

best regards!

Hi @LordLiverpool,
I think the ip is bad (see @jgreco comment about).
I just updated the dorita980 code to support dgram in node 4 (you are using node v4, rigth?).
Try again now updating dorita980 to latest version (2.4.1) in your project, where you has the getRobotIp() code:

npm install dorita980@2.4.1
node yourtest.js

But i recomend to you install node 6, btw.

best regards.

@facu I went as far as installing dorita980 and node so npm commands worked so I think I installed properly the packages, I mean that I can do an npm list and see them. After that, the tutorial (found on https://www.npmjs.com/package/dorita980) suddenly shows some code to implement (WHERE???) to send commands. I donā€™t have the slightest idea of the interface Iā€™m supposed to use with this code? Where should I write this code to so it sends the commands? It doesnā€™t look like any kind of groovy / smartthings compatible code. Is it for a different platform? And if so, why this page assumes we all know what it should be? If you look at the tuto on how to install dorita on windows it gives a link that leads to a tree of hundreds of folders and subfolders with no explanation whatsoever. This is really weirdā€¦ So I eventually did some more research and found out that I had to install some stuff and I did it, as described above and now dorita is supposedly installed onto my PCā€¦ but I have no idea what to do nextā€¦ anyway, if you can help, thatā€™s great! :slight_smile: I think I spent too many hours on this now and I should sleep because Iā€™m not being productive at all, as it is obvious hereā€¦

Best,
ElfĆØge.

@facu I was able to send a command through runkitā€¦ now I need to find out how to do that from a different platformā€¦

Hi,
You were right about the IP, it was 192.168.0.23.
And Iā€™ve now got the password :slight_smile: Thanks for your great help.
Incidentally I canā€™t use node 6 because I have XP (yeah, I know, but changing is such a pain).

Ok I eventually figured that I just had to create a java fileā€¦ it was that simpleā€¦ lol

1 Like

Hi @Elfege_Leylavergne,
dorita980 is a Node.js module. Node.js is across-platform Javascript runtime environment for developing a diverse variety of tools and applications. While JavaScript engines are traditionally run in Web browsers, the Node.js libraries are focused on building server-side applications in JavaScript. And Javascript has nothing to do with Java. Are two different languages (you are creating javascript file to put code inside to use external module (dorita980) to do what you want).
dorita980 itself has nothing to do with SmartThings for now because SmartThings device driver plugin system use python as languaje to code connectors. SmartThings has plans to extend his plugin systen to use node.js modules in near future.
So, today dorita980 is useful with other home automation systems based on node.js like homeBridge or custom home automation projects. Is just a SDK in nodejs to control the robot. You can use it to do what you want.
(and is useful to get your password easly as well)

regards,

Iā€™ve just successfully got my Zipabox home automation to control the Roomba. From that Zipabox you can issue HTTP requests so thatā€™s what I do, having pieced together whatā€™s needed from your posts here and a few things elsewhere. I only used dorita to get the IP and then the user and password.
I imagine many other home automation systems could do something similar.
Many thanks!

Yeah I had to figure that out too. Create a .js file and run it with the node command from a command prompt.

Yes and so far I could not integrate this with smartthings. I know itā€™s not supposed to be possible but I am looking for a work around by creating a node http server. So far I could access the http server remotely but I canā€™t get it to execute my js filesā€¦

@facu : thanks for the time you took to answer! I really appreciate. I have been working a lot on this now and I canā€™t get any js file to be run remotely. I tried creating batch files, and they work and run the roomba but now the trick is to have them run remotely.

Maybe a listening script would be the solution: having a script onto the http server listening for incoming data and triggering the js or batch file (containing the node file.js command) accordingly. Any idea ? I tried with http dispatcher but so far I canā€™t get it to workā€¦ because I have no idea of the code I need to write so when a /GET/string command arrives it runs the batch file, which would definitely be a solution allowing to work with smartthings local http requests.

Ok, Iā€™m giving up, for now, on trying to use nodejs files, although it was great to introduce myself to it for I had not wrote some actual javascript in a long, long timeā€¦ and Iā€™m amazed by how much I forgot, almost everything! Not that I knew a lot in the first place though.

Since it is not yet directly compatible with ST I am still to find a way to send the httpget() command properly. What is MOST frustrating is that it used to work without requesting the asset-id and now Iā€™m lost trying and trying over to send the headers.

I was wondering if someone knew how the local url would look likeā€¦ I tried to figure this out out of the javascript from dorita980ā€™s local.js but to no avail. I suppose it could look like http://IP:443/. I suppose roomba should have its own http(s) service running since it receives commands from the cloud on port 443, right?

Any bit of indication / help, even apparently useless here will be welcome! :smiley:

Best regards,
ElfĆØge.

Didnā€™t I see somewhere (maybe here) that the local URL would be:

https://:443/umi

?

it is the case indeed just saw it here and in the local.js of dorita980.

and then the path is the same ? /umi/?blid= etc?

1 Like

I guess so, although I havenā€™t tried it.
The beer, sadly, will be difficult since I live in Spain :slight_smile:

`

It works!

`

`

 /**
*  Virtual Switch 
*
*  Copyright 2016 Elfege
*
*  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
*  in compliance with the License. You may obtain a copy of the License at:
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
*  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
*  for the specific language governing permissions and limitations under the License.
*
*/
metadata {
    definition (name: "ROOMBA switch", namespace: "Elfege", author: "Elfege") {
        capability "Switch"
        capability "Refresh"     
        command "dock"
        command "resume"
        command "pause"
    }
}
// simulator metadata
simulator {
}
// UI tile definitions
tiles {
    standardTile("CLEAN", "device.switch", width: 1, height: 1, canChangeIcon: false) {
        state "released", label: 'Clean', action: "switch.on", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff", nextState: "pressed"
        //state "pressed", label: 'Cleaning', action: "refresh.refresh", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "released"
    }
    standardTile("STOP", "device.switch", width: 1, height: 1, canChangeIcon: false) {
        state "released", label: 'Stop', action: "switch.off", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff", nextState: "pressed"
        state "pressed", label: 'Stopping', action: "", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "released"
    }
    standardTile("PAUSE", "device.switch", width: 1, height: 1, canChangeIcon: false) {
        state "released", label: 'pause', action: "pause", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff" //,  nextState: "pressed"      
        state "pressed", label: 'pausing', action: "", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "released"
    }
    standardTile("RESUME", "device.switch", width: 1, height: 1, canChangeIcon: false) {
        state "released", label: 'Resume', action: "resume", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff" //,  nextState: "pressed"      
        state "pressed", label: 'resuming', action: "", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "released"
    }
    standardTile("DOCK", "device.switch", width: 1, height: 1, canChangeIcon: false) {
        state "released", label: 'Dock', action: "dock", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff", nextState: "docking" 
        state "pressed", label: 'Docking', action: "", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "on"
    }
    standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
        state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
    }        
    main "CLEAN"
    details(["CLEAN","STOP","PAUSE", "RESUME", "DOCK", "refresh"])
}
def parse(description) {
    def msg = parseLanMessage(description)
    def headersAsString = msg.header // => headers as a string
    def headerMap = msg.headers      // => headers as a Map
    def body = msg.body              // => request body as a string
    def status = msg.status          // => http status code of the response
    def json = msg.json              // => any JSON included in response body, as a data structure of lists and maps
    def xml = msg.xml                // => any XML included in response body, as a document tree structure
    def data = msg.data              // => either JSON or XML in response body (whichever is specified by content-type header in response)
}
def sendRequest() {
    state.user = "xxxxxxxxxxxxxxxxxxx"
    state.password = "xxxxxxxxxxxxxxxxxxx"
    state.AssetID = "ElPaso@irobot!xxxxxxxxxxxxxxxxxxx" // replace xxx by your robotā€™s user name 
    state.Authorization = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"  // this is a base64 encoding of the string  "user:" + the rotbotā€™s password  See http://www.url-encode-decode.com/base64-encode-decode/
// Haven't figured out the local command yet   
// state.deviceNetworkId = "728A8678:1BB"  //  "1921681016:443" hex conversion
    //state.ip = "192.168.10.16:443"
    //state.host =  '${state.ip}:443' /*"https://irobot.axeda.com:443" */
    state.path = "/umi/?blid=${state.user}&robotpwd=${state.password}&method=multipleFieldSet&value=%7B%0A%20%20%22remoteCommand%22%20:%20%22${state.RoombaCmd}%22%0A%7D" 
    def httpRequest = [
        method:"GET",
        uri: "https://irobot.axeda.com/services/v1/rest/Scripto/execute/AspenApiRequest?blid=${state.user}&robotpwd=${state.password}&method=multipleFieldSet&value=%7B%0A%20%20%22remoteCommand%22%20:%20%22${state.RoombaCmd}%22%0A%7D",   
        //strictSSL: false,
        headers:	[
            'User-Agent': 'aspen%20production/2618 CFNetwork/758.3.15 Darwin/15.4.0',
            Accept: '*/*',
            'Accept-Language': 'en-us',
            'ASSET-ID': state.AssetID,            
        ]
    ]
    try {
        httpGet(httpRequest) { 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 dock() {
    sendEvent(name: "switch", value: "on")
    log.debug "Roomba Switch is --------------------------docking"
    state.switch  = "pressed"
    log.debug "Running Roomba. Roomba's ID is $state.ID"
    state.RoombaCmd = "dock" 
    sendRequest() 
}
def on() {
    sendEvent(name: "switch", value: "cleaning")
    log.debug "Roomba Switch is --------------------------on"
    state.switch  = "pressed"
    log.debug "Running Roomba"
    state.RoombaCmd = "start" 
    sendRequest() 
}
def resume() {
    sendEvent(name: "switch", value: "resume")
    log.debug "Roomba Switch is --------------------------resume"
    state.switch  = "pressed"
    log.debug "Running Roomba"

    state.RoombaCmd = "resume" 
    sendRequest() 
}
def off() {
    sendEvent(name: "switch", value: "off")
    log.debug "Roomba Switch is --------------------------on"
    state.switch  = "pressed"
    log.debug "Stopping Roomba"

    state.RoombaCmd = "stop" 
    sendRequest() 
}
def pause() {
    sendEvent(name: "switch", value: "off")
    log.debug "Roomba Switch is --------------------------off"
    state.switch  = "pressed"
    log.debug "Running Roomba"
    state.RoombaCmd = "pause" 
    sendRequest() 
}
indent preformatted text by 4 spaces
2 Likes

Good job. Today my Roomba also successfully cleaned the house under the command of my Zipabox.

Reviving this thread :slight_smile:
Out of curiosityā€¦ read through this entire thread, awesome to see you rockstars got it reversed engineered.
I am wondering if anyone got it working with ST hub itself, and what are the options you could do with it after integration?
just got my 980 and was wonderingā€¦

It works just fine within Smartthings, it is a very simple protocol.

Can someone explain to me what zipabox is and why we are talking about it on the ST forum?

Thank you for your response Sir . Would you mind (if you have the time) to type out the steps needed to get this integrated in ST? i am a bit confused as to what needs to be done first to get it into ST.
it will be highly appreciated for everyone reading this thread now or in the future.

1 Like

Can someone please, pretty please let us know the steps for integration :slight_smile: