dorita980 icon indicating copy to clipboard operation
dorita980 copied to clipboard

Added support for FW version 3 and Braava m6

Open peci1 opened this issue 4 years ago • 32 comments

I got Roomba i7 and Braava m6, both with firmware 3.2. I needed to do some changes to support FW version 3, and also to support Braava robots. I'm not sure I captured all the differences between v2 and v3, and between roomba and braava robots. But I think this could be a good start.

This might also be a solution for https://github.com/koalazak/rest980/issues/44 .

peci1 avatar Dec 26 '19 01:12 peci1

nice! @peci1 can you update the readme to reflect the new version option. Also update the package.json to increase the minor version value? is npm run test passing with your changes?

Also, I don't own a m6 robot to test your changes, So to be cautious I would like someone else to test and confirm that all this branch is working with several robots models.

I'm going to leave the PR open until someone else tests this, ok? HELP WANTED!

thank you very much!

koalazak avatar Jan 11 '20 14:01 koalazak

I fixed a few style hints from the linter. Otherwise, tests run fine.

peci1 avatar Jan 14 '20 01:01 peci1

@peci1, does updated getMission() work with your i7/m6? It doesn't work for me (on my i7/m6), will check this more deeply tomorrow.

Also I try to get info required for cleanRoom, but my m6 reports lasCommand data this way:

lastCommand: {
    command: 'start',
    initiator: 'rmtApp',
    time: 1579099749,
    ordered: 0,
    pmap_id: '1lHG_7bGROmxe5Tcg8U1qQ',
    regions: [ [Object] ],
    user_pmapv_id: '200112T134556'
  },

How can I decode regions data?

thorazine-12 avatar Jan 20 '20 22:01 thorazine-12

What do you mean by updated getMission()? Did you try with the robots activated (i.e. not sleeping) and parked in home base? For me, getMission() doesn't work on sleeping robots. Try to give it a task with your phone, then stop them, park them, and try getMission(). Or you can even try calling getMission() during an actual mission, that should work. On my irobots, the experimental map feature works, and it is based on getMission().

Also, are you sure your robots have up-to-date firmware?

Do you try using the API directly or via rest980? If latter, could test with https://github.com/koalazak/rest980/pull/46 ?

About regions: I haven't investigated that yet...

peci1 avatar Jan 20 '20 23:01 peci1

Well I think that I have newest firmware: 3.2.4 (Roomba) and 3.2.5 (Braava). In fact getMission is not very important to me but cleanRoom is essential - especially for using m6. Both i7 and m6 return [Object] data in the regions field - here is what I get from Roomba:

  lastCommand: {
    command: 'start',
    initiator: 'localApp',
    time: 1579632664,
    ordered: 0,
    pmap_id: 'RG3hl73tTq2vpwljyUv8yw',
    regions: [ [Object] ],
    user_pmapv_id: '200116T202620',
    event: null
  }

As to the getMission() - I've checked and getRobotState(['pose']) wait until robot starts its mission, if it is in dock/pause/stop it doesn't return any values.

Another issue: when I do getRobotState(['mopReady']) on m6 with updated dorita, I have:

pi@raspberrypi:~/www/plugins $ node test.js mopready
undefined

when I execute the same command, but with original (master) branch, I get:

pi@raspberrypi:~/www/plugins $ node test.js mopready
{ tankPresent: true, lidClosed: true }

EDITED: I've just solved problem with [Object] reply in regions field :) It seems that [Object] reply is returned when I do getRobotState with more than 1 params, for example when I execute getRobotState['cleanMissionStatus', 'lastCommand']. But with getRobotState['lastCommand'] I get proper values:

{
  command: 'start',
  initiator: 'rmtApp',
  time: 1579099749,
  ordered: 0,
  pmap_id: '1lHG_7bGROmxe5Tcg8U1qQ',
  regions: [
    {
      region_id: '23',
      region_name: 'Kuchnia',
      region_type: 'dining_room'
    }
  ],
  user_pmapv_id: '200112T134556'
}

thorazine-12 avatar Jan 21 '20 19:01 thorazine-12

I find out why dorita generated 'undefined' reply for some request. It seems that my braava was not recognized correctly in local.js. Since I don't know how to fix it, I just changed this

  const modelProps = {
    'roomba': ['bin'],
    'braava': ['detectedPad', 'mopReady', 'padWetness']
  };

to this:

  const modelProps = {
    'roomba': ['bin'],
    'braava': ['blahblah']
  };

to make sure I can get with getRobotState any field I want. And now it works, I can get info about mopReady and detectedPad with no problem.

I don't know why m6 is not recognized. This is what I get with getRobotState(['sku']):

pi@raspberrypi:~/www/plugins $ node irobot.js roomba get sku
i755040
pi@raspberrypi:~/www/plugins $ node irobot.js braava get sku
m613840

thorazine-12 avatar Jan 24 '20 23:01 thorazine-12

@thorazine-12 could you please try getRobotState() on all of 'detectedPad', 'mopReady', 'padWetness' separately? So that we can know which one of these doesn't work for you...

peci1 avatar Jan 29 '20 01:01 peci1

The problem is that there is no 'pose' information when it is not cleaning. I have confirmed it with my braava m6.

sohsatoh avatar Aug 31 '20 00:08 sohsatoh

I tested it by editing the local.js file as follows

...
// Added
 const defaultPose= {
    "theta":0,
    "point": {
      "x":0,
      "y":0
    }
  }
...
  client.on('packetreceive', function (packet) {
    if (packet.payload) {
      try {
        const msg = JSON.parse(packet.payload.toString());
        robotState = Object.assign(robotState, msg.state.reported);
        if(!robotState.pose) {
         // Added
          console.log('pose could not find');
          robotState.pose = defaultPose;
        }
        client.emit('update', msg);
        client.emit('state', robotState);
        if (robotState.cap) {
          cap = {};
          cap = Object.assign(cap, robotState.cap);
        }
        if (robotState.sku && robotState.sku.toLowerCase().startsWith('m6')) {
          model = 'braava';
        }
      } catch (e) {}
    }
  });

Then getMission worked fine. This is not a beautiful fix though.

To my understanding, the way of Braava m6 locating their position is based on the position where it docked at home base. When Braava m6 is docked, the positional values will be 0 for both angles, x, and y. So there is no need to return location information.

However, on my Roomba 960, here's the information when it's docked. pose":{"theta":67,"point":{"x":-5,"y":-4}} Presumably, all models with the Imprint Smart Mapping feature should return information like that, but I only have the Roomba 960 and Braava m6, so I can't be sure.

sohsatoh avatar Aug 31 '20 01:08 sohsatoh

Just curious, what's holding this up from getting merged? Really wish this project supported braava... seems like there hasn't been much movement other than a few readme updates lately.

dcmeglio avatar Sep 08 '20 23:09 dcmeglio

Hello @dcmeglio, I will merge the PR when the author and several people who own both type of robots (vaccum and mop) confirm that both robots works fine together at the same time and all features works fine at least in vaccum robot. At the moment looks like nobody confirm this ☝️ and there is no a solid PR, just suggestions and troubleshooting. I do not own a bravaa so I can't test on my side to support it :/

donations are welcome to help me to get extra robots and extend features of this library: https://opencollective.com/dorita980

thank you

koalazak avatar Sep 09 '20 02:09 koalazak

I tested it by editing the local.js file as follows

...
// Added
 const defaultPose= {
    "theta":0,
    "point": {
      "x":0,
      "y":0
    }
  }
...
  client.on('packetreceive', function (packet) {
    if (packet.payload) {
      try {
        const msg = JSON.parse(packet.payload.toString());
        robotState = Object.assign(robotState, msg.state.reported);
        if(!robotState.pose) {
         // Added
          console.log('pose could not find');
          robotState.pose = defaultPose;
        }
        client.emit('update', msg);
        client.emit('state', robotState);
        if (robotState.cap) {
          cap = {};
          cap = Object.assign(cap, robotState.cap);
        }
        if (robotState.sku && robotState.sku.toLowerCase().startsWith('m6')) {
          model = 'braava';
        }
      } catch (e) {}
    }
  });

Then getMission worked fine. This is not a beautiful fix though.

To my understanding, the way of Braava m6 locating their position is based on the position where it docked at home base. When Braava m6 is docked, the positional values will be 0 for both angles, x, and y. So there is no need to return location information.

However, on my Roomba 960, here's the information when it's docked. pose":{"theta":67,"point":{"x":-5,"y":-4}} Presumably, all models with the Imprint Smart Mapping feature should return information like that, but I only have the Roomba 960 and Braava m6, so I can't be sure.

Your fix doesn't work for me, the m6 does not respond to getmission

dcmeglio avatar Sep 13 '20 19:09 dcmeglio

I've implemented the fix with returning zero pose when braava is sleeping. getMission() should now return even in the sleeping robot case.

peci1 avatar Sep 26 '20 19:09 peci1

I don't understand your problems, @thorazine-12 . You say you're using getRobotState() and the results differ depending on how many fields you wait to. But getRobotState() always returns all fields, so it can't depend on the number of passed fields. It seems to me that you had to use some custom function to query the robot state. Anyways, the problem with [ [Object] ] is most probably just a visualization issue. It wouldn't matter if you'd parse the reponse in javascript. If you print the value and parse the printed value, then you need to go one level deeper in the object structure and print e.g. actualState['lastCommand']['regions'] instead of actualState['lastCommand']. Or you can use any of the methods mentioned here: https://stackoverflow.com/questions/10729276/how-can-i-get-the-full-object-in-node-jss-console-log-rather-than-object .

peci1 avatar Sep 26 '20 21:09 peci1

I'm not sure what was the reason @thorazine-12's braava was incorrectly detected, as the detection is done based on SKU, and his SKU also starts with m6, but I've added an option to pass the robot model as a parameter to the Local V2 API.

peci1 avatar Sep 26 '20 21:09 peci1

Another issue: when I do getRobotState(['mopReady']) on m6 with updated dorita, I have:

pi@raspberrypi:~/www/plugins $ node test.js mopready
undefined

@thorazine-12 in this part, you actually query mopready and not mopReady. That would explain the error...

peci1 avatar Sep 26 '20 21:09 peci1

This is the output of my i7 when I query multiple fields and print just the lastCommand - I see no problem:

 myRobotViaLocal.getRobotState(['lastCommand', 'cleanMissionStatus']).then((actualState) => {
        lastCommand = actualState['lastCommand'];
        console.log(lastCommand);
  });
{
  command: 'start',
  initiator: 'localApp',
  time: 1601201384,
  ordered: 1,
  pmap_id: 'bkG1Rw8DQ1-vlGddUgnyyg',
  regions: [ { region_id: '1', type: 'rid' } ],
  user_pmapv_id: '191223T134605'
}

peci1 avatar Sep 27 '20 10:09 peci1

It seems that braava m6 does not include regions in lastCommand at all. But that also might be given by the fact that my braava has only maps each with a single region...

peci1 avatar Sep 27 '20 10:09 peci1

When I use rest980 and go to /api/local/info/mission for my m6, the browser just spins and doesn't load. Works fine for both of my i7's though. /api/local/info/state works fine for the m6. Nothing in syslog when I call mission. I'm using your latest code. Anything I can do to help track this down?

dcmeglio avatar Sep 29 '20 01:09 dcmeglio

Maybe the autodetection of robot type also doesn't work for you? You can try creating the local API with

myRobotViaLocal = Local(username, password, ip, 800, 3, "braava");

Also, are all of 'detectedPad', 'mopReady', 'padWetness' present in your state?

peci1 avatar Sep 29 '20 02:09 peci1

I’ll have to test tomorrow when I’m at a pc, but yea those three values show up in the state output. And FYI my sku starts with m6

dcmeglio avatar Sep 29 '20 02:09 dcmeglio

@peci1 sorry I never replied. No, when I change to using what you pasted above it still doesn't work. As I said though, all those values appear in state. Calling /api/local/info/mission for my two i7s works fine though. Anything else I can do to help debug?

dcmeglio avatar Oct 24 '20 14:10 dcmeglio

How do you actually test the changes? Do you have a rest980 project checked out in some directory and edit dorita980 directly in the node_modules subdirectory? Also, do you test rest980 with the changes from koalazak/rest980#46 ?

peci1 avatar Oct 25 '20 23:10 peci1

Yes to both your questions.

dcmeglio avatar Oct 26 '20 03:10 dcmeglio

@dcmeglio Please make sure your server address is not the same as the Roomba's, etc. It works fine in my environment. {"cleanMissionStatus":{"cycle":"none","phase":"charge","expireM":0,"rechrgM":0,"error":0,"notReady":31,"mssnM":24,"sqft":75,"initiator":"rmtApp","nMssn":69},"pose":{"theta":0,"point":{"x":0,"y":0}},"batPct":100,"detectedPad":"reusableWet","mopReady":{"tankPresent":false,"lidClosed":true},"padWetness":{"disposable":2,"reusable":2}}

sohsatoh avatar Oct 26 '20 06:10 sohsatoh

If the ip were the issue, then no endpoint would work. Only the mission endpoint fails for me

dcmeglio avatar Oct 27 '20 01:10 dcmeglio

@dcmeglio Did you replace dorita980 in node_modules of rest980 properly? I can't think of any other reason other than it's loading dorita980 from a higher level directory, etc.

sohsatoh avatar Oct 27 '20 09:10 sohsatoh

@dcmeglio Did you replace dorita980 in node_modules of rest980 properly? I can't think of any other reason other than it's loading dorita980 from a higher level directory, etc.

You were absolutely right. There was a local copy I didn’t see causing the issue. Working fine now!

dcmeglio avatar Nov 11 '20 04:11 dcmeglio

@dcmeglio Great! Thanks for reporting.

peci1 avatar Nov 11 '20 04:11 peci1

I replaced #103 in https://github.com/koalazak/rest980/pull/46. Getting this error with "firmwareVersion": 3,:

> [email protected] start /home/pi/rest980
> node ./bin/www

/home/pi/rest980/node_modules/dorita980/lib/v2/cloud.js:7
  throw new Error('Not implemented.');
  ^

Error: Not implemented.
    at dorita980 (/home/pi/rest980/node_modules/dorita980/lib/v2/cloud.js:7:9)
    at new cloud (/home/pi/rest980/node_modules/dorita980/index.js:12:10)
    at /home/pi/rest980/routes/api.js:33:46
    at handleIP (/home/pi/rest980/routes/api.js:26:68)
    at Object.<anonymous> (/home/pi/rest980/routes/api.js:27:1)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Module.require (internal/modules/cjs/loader.js:887:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.<anonymous> (/home/pi/rest980/app.js:11:16)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Module.require (internal/modules/cjs/loader.js:887:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.<anonymous> (/home/pi/rest980/bin/www:7:11)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `node ./bin/www`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/pi/.npm/_logs/2021-06-21T09_29_14_385Z-debug.log```

SerhiiAksiutin avatar Jun 21 '21 09:06 SerhiiAksiutin