Neato login (node.js conversion)

Hi all,

I’m trying to convert the following Postman Pre-request Script code to a Nymea script:

  var moment = require('moment')
  var serial = 'your_robot_serial';
  var secret_key = 'your_robot_secret_key';
  var body = '{"reqId":"1", "cmd":"getRobotState"}';
  
  var date = moment.utc().format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
  var message = serial.toLowerCase() + '\n' + date + '\n' + body;
  var signature = CryptoJS.HmacSHA256(message, secret_key);
  signature = escape(signature);
  
  pm.environment.set('body', body);
  pm.environment.set('mom_ts', date);
  pm.environment.set('signature', signature);

I struggle with the node.js components however. How can I rewrite this script to work in Nymea?

Thanks in advance!

Best,
Rob

Hey @loosrob,

So, nymea doesn’t support node.js but requires ECMA6 standard JavaScript for plugins. I have checked the official Neato github repo and there seems to be JS lib, but that one requires a browser window which isn’t really an option either.

Now, what one could do, is to walk through the JS code there and transform it into standard compliant JS without dependencies to node.js or a webbrowser. However, another option would probably be to use this python library: https://github.com/stianaske/pybotvac

For python plugins, there are no such restrictions and virtually any python code should run, including this neato lib. Here would be a minimalistic example for a python plugin: https://github.com/nymea/nymea-plugins/blob/master/sunposition/

If that’s an option for you, I’ll be happy to help you further with getting this going.

Hi @mzanetti,

Thanks for the quick reply. I don’t know any python, and only some javascript, which I picked up along the way. Happy to try to pick up some python as well, but I would sure be grateful for any help you could give - both with python and plugin development for Nymea, as so far I’ve only written some straightforward JS scripts.

I’ll have a look and will see if I can wrap my head around this :slight_smile:

Would it be easier to use the js sdk provided by Neato?

I’ve checked this too but it seems it’s based on jQuery which requires a browser window and DOM… So not really an option either to use in a backend plugin…

Hey @loosrob,

I’ve had a go with that pybotvac library and it seems to work smooth so far. I’ve created an initial plugin and an account at neato. So far I can successfully log in and fetch the list of robots, which, for me, are none… So that’s as far as I can test…

I’ve added a README.md with instrcutions on how to test it… If the untested code works, you even should already be able to start and stop cleaning but again, I don’t have such a robot so I couldn’t test this.

Would be cool if you could test this and extend the code to support more features. Do ask if you have more questions.

Hi @mzanetti

Super cool!
I just wanted to try, but got the following when going through the pip3 step:

In --require-hashes mode, all requirements must have their versions pinned with ==. These do not:
typing-extensions>=3.6.4; python_version < “3.8” from https://files.pythonhosted.org/packages/60/7a/e881b5abb54db0e6e671ab088d079c57ce54e8a01a3ca443f561ccadb37e/typing_extensions-3.7.4.3-py3-none-any.whl#sha256=7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918 (from importlib-metadata==3.7.0->-r requirements.txt (line 55))
THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.

Mismatches for: launchpadlib, lazr.uri, wadllib, lazr.restfulclient, cryptography, cffi.

And then the following in the last step:

nymea@nymea:~ $ NYMEA_PLUGINS_PATH=/nymea-plugin-neato nymead -n -d Neato -d PythonIntegrations

Translations: Could not find nymead translations for “en_GB”

“/usr/translations”

“/usr/share/nymea/translations”

I | Application: =====================================

I | Application: nymead 0.26.0+202102251126~buster+rpi1 started with user ID 1000

I | Application: =====================================

W | DBus: Failed to register D-Bus service.

W | DBus: Failed to register D-Bus service.

W | UserManager: Failed to register PushButton D-Bus service.

W | default: QFSFileEngine::open: No file name specified

W | ServerManager: Could not open “” : “No file name specified”

W | ServerManager: Using fallback self-signed SSL certificate: “/home/nymea/.local/share/nymea/certs/nymead-certificate.crt”

W | PlatformZeroConf: QtAvahiServicePublisher(0x673978) error: Bad state

W | WebSocketServer: Error listening on “wss://0.0.0.0:4444”

W | WebServer: Webserver could not listen on “http://0.0.0.0:80” “The address is protected”

W | WebServer: Webserver could not listen on “https://0.0.0.0:443” “The address is protected”

W | nymea.mqtt.server: Error listening on port 1883

W | Mqtt: Error starting MQTT server on port 1883

W | DBus: Failed to register D-Bus service.

W | DBus: Failed to register D-Bus service.

W | DBus: Failed to register D-Bus service.

W | DBus: Failed to register D-Bus service.

W | DBus: Failed to register D-Bus service.

I | qt.bluetooth.bluez: Missing CAP_NET_ADMIN permission. Cannot determine whether a found address is of random or public type.

W | DBus: Failed to register D-Bus service.

W | DBus: Failed to register D-Bus service.

W | NetworkManager: Scan error: “org.freedesktop.NetworkManager.PermissionDenied” “org.freedesktop.NetworkManager.network-control request failed: not authorized”

And the command hangs at this point.

Ok. Need to fix the requirements.txt, the raspberry pi has different repositories than standard debian which might cause this.

Anyhow, for now you can install the dependencies manually with

pip3 install -t modules pybotvac

Then the plugin should load.

Also, the command doesn’t hang, it’s just running… You can connect to it with the app and set up the stuff.

As you’re running it on the pi, you’ll probably first want to stop the running namea service with

sudo systemctl stop nymead

And also use sudo to start nymead manually so it’ll pick up the system config files.

Thanks for the quick reaction - the manual install of the dependencies worked fine indeed. I now have a subfolder “modules” in the plugin folder with pybotvac etc. (The plugin folder also contains the integrationpluginneato.json and integrationpluginneato.py files.

Then I stopped the running nymead service and started another to test the plugin:

sudo NYMEA_PLUGINS_PATH=/home/nymea/nymea-plugin-neato nymead -n -d Neato -d PythonIntegrations -d ThingManager

But the plugin throws an error:

 I | ThingManager: Loading plugins from: "/home/nymea/nymea-plugin-neato"
 W | ThingManager: Error importing python plugin from: "/home/nymea/nymea-plugin-neato/integrationpluginneato.py"
Traceback (most recent call last):
  File "/home/nymea/nymea-plugin-neato/integrationpluginneato.py", line 2, in <module>
    from pybotvac import Account, Neato, OAuthSession, PasswordlessSession, PasswordSession, Vorwerk, Robot
  File "/home/nymea/nymea-plugin-neato/modules/pybotvac/__init__.py", line 5, in <module>
    from .version import __version__
  File "/home/nymea/nymea-plugin-neato/modules/pybotvac/version.py", line 1, in <module>
    import pkg_resources
ModuleNotFoundError: No module named 'pkg_resources'
Exception ignored in: <generator object path at 0x6e62d1b0>
Traceback (most recent call last):
  File "/usr/lib/python3.7/importlib/resources.py", line 190, in path
NameError: name 'FileNotFoundError' is not defined
 W | ThingManager: Error loading plugin: "/home/nymea/nymea-plugin-neato/integrationpluginneato.py"

Do I need to enter my account info in a config file somewhere?

Right, you’d also need to

pip3 install -t modules setuptools

I /think/ that should be all you need (i.e. all the rest should be pulled in automatically as dependency)… if you still get errors about missing modules, check the requiremts.txt, that lists all the dependencies of the lib.

Hey,
I’ve fixed the requirements.txt to also work on raspbian now.

should be myThings() with an s…

That did the job! Many thanks!

I can now see the robot, and start & stop cleaning. I’ll see if I can add other actions, such as pause/resume and dock.

awesome!

Looking into the python lib here: https://github.com/stianaske/pybotvac/blob/master/pybotvac/robot.py#L57

I can see that there’s also the possibility to find out if it’s currently charging, docked etc. For those you’d add new stateTypes to the json file and then use thing.setStateValue() to set the states. Probably it could make sense to set up a polling loop for updating those states every x seconds (not sure if there’s an API rate limit on the API). Check out the sunposition plugin for an example on how to set up such a polling loop.

Same for actions, check the robot.py for functions you can call and add according actionTypes to the json file. You can then act on them in executeAction.

Thanks - I’ll see if start/pause/resume could be one action, based on the state, as at any time only one of those actions should be available.

I created a fork to test the merging of the “start/pause/resume” command, and added a “go to base” command (which I prefer over the stop command that just stops the robot where it is, and leaves it there). This seems to work fine for my robot, but somehow it starts the cleaning without “no go” lines enabled, so I’ll dig into that a bit further.

(Since I’m new to git: should I have created a branch or a fork?)

Is it possible to add buttons to the thing icon (e.g. “play/pause”, “home” (for docking) and “stop”), similar to the power switch and dimmer for lights?

A fork on github is fine. Eventually, when this matures we’ll create a pull request for the upstream nymea-plugins repo.

I have also worked on a branch meanwhile which uses OAuth for logging in (instead of just saving user/password as thing parameters). I had to extend the Python API a little though, so that change requires nymea experimental for now.

Also, IMO it would make sense to provide a stop and a go-to-base action. While you’re right that normally you want it to return to the base, sometimes you just want it to shut up right away, for example when a phone call comes in. In general I’d say you should provide most actions and states that the robot can give you. Later we’ll create a pretty UI in nymea app for vacuum cleaners which would then just present the most used/wanted features, but still allowing full detailed control in the Details view.

Completely agree - we have the pause and stop functions for events such as phone calls now.Phone calls are in fact one of the main reasons I wanted to be able to control the vacuum cleaner from the Nymea screen :slight_smile:

The difference between pause and stop is that you can resume where you left of when you paused, but not when you stopped - stopping is final.

Thanks again for your help!

I still need to figure out why the no-go lines aren’t enabled, because if I understand the pybotvac code correctly, it will try to start in that mode first, but somehow doesn’t for my robot.

I did implement some states, that are updated when the plugin is loaded and periodically with a timer:


Not sure how to interpret the Docked status: robot returns “isDocked: true” even though the robot isn’t connected to it’s docking station.

There appear to be some other issues as well:

  • Logging indicates "expand thread pool for plugin “Neato” to 3 (then 4, 5 and 6)
  • List of things contains 3 “Fifi” things (yes, the name of the robot is Fifi :slight_smile:)
  • The polling timer seems to stop at some point. Could this be related to the thread increase?

Could the timer and pollService that is activated via that timer conflict with the setupThing?