18 Commits

Author SHA1 Message Date
naofireblade
d6dd94979b Offical support for D5 Connected 2017-05-20 13:34:21 +02:00
naofireblade
6deca89d27 Updated dependencies 2017-05-18 19:14:59 +02:00
naofireblade
5eb5b9934d Support for basic-2 services 2017-05-18 18:59:14 +02:00
naofireblade
9a5c3f942c Error Handling 2017-05-13 21:17:46 +02:00
naofireblade
8f6f3dc72d Wording 2017-05-13 21:15:13 +02:00
naofireblade
0f8fb1dedf Wording 2017-05-07 17:20:54 +02:00
naofireblade
e01fb8df28 Bugfixes and changes in periodic refresh
* periodic refresh is now optional (minimum 60s)
* refresh while cleaning is now 30s
* fixed a bug where the robot starts to drive home after some minutes of
cleaning
2017-05-07 16:31:41 +02:00
naofireblade
8cb6ecf8a5 Improved refresh parameter 2017-05-06 23:57:36 +02:00
naofireblade
36e8496108 Version 0.3.0
* Added periodic refresh of robot state
* Improved go to dock switch to be enabled as soon as possible without
manual refresh
* Improved switches to indicate the time an action lasts
* Improved eco mode to not be overwritten by robot state
2017-05-06 23:51:58 +02:00
naofireblade
b2ddecc827 Improved go to dock command 2017-05-04 19:24:38 +02:00
naofireblade
5dbdb18c58 Set theme jekyll-theme-cayman 2017-04-30 18:07:17 +02:00
naofireblade
d49cab1e97 Set theme jekyll-theme-cayman 2017-04-30 18:06:45 +02:00
naofireblade
92eabfe5cd Wording 2017-04-30 14:35:23 +02:00
naofireblade
67b930653f Wording 2017-04-30 14:17:21 +02:00
naofireblade
579bd32a93 Readme 2017-04-28 18:12:22 +02:00
naofireblade
a606d4c632 Version 0.2.0
* Added dock info
* Changed logging to debug library
2017-04-22 22:57:09 +02:00
naofireblade
3a4327f7e9 spelling 2017-04-18 02:59:07 +02:00
naofireblade
cbfbb26b14 spelling 2017-04-18 02:51:59 +02:00
7 changed files with 262 additions and 186 deletions

17
.gitattributes vendored
View File

@@ -1,17 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

47
.gitignore vendored
View File

@@ -1,47 +0,0 @@
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# =========================
# Operating System Files
# =========================
# OSX
# =========================
.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

View File

@@ -4,4 +4,26 @@
* Added return to base
* Added enable and disable schedule
* Added enable and disable eco mode
* Added battery info
* Added battery info
## 0.2.0
* Added dock info
* Improved logging to use a debug library
## 0.2.1
* Improved the go to dock command
## 0.3.0
* Added periodic refresh of robot state while cleaning
* Added optional periodic refresh of robot state while not cleaning
* Added error messages when cant login or get robot
* Improved go to dock switch to be enabled as soon as possible without manual refresh
* Improved switches to indicate the time an action needs to complete
* Improved eco mode to not be overwritten by robot state update
## 0.3.1
* Added experimental support for newer Neato Botvac models

View File

@@ -1,40 +1,51 @@
# homebridge-neato
This is a plugin to control your [Neato](https://www.neatorobotics.com/) vacuum robot.
This is a plugin for [homebridge](https://github.com/nfarina/homebridge) to control your [Neato](https://www.neatorobotics.com/) vacuum robot. You can download it via [npm](https://www.npmjs.com/package/homebridge-neato).
# Installation
1. Install homebridge using: npm install -g homebridge
2. Install this plugin using: npm install -g homebridge-neato
3. Update your configuration file. See the sample below.
# Configuration
Configuration sample:
Add the following information to your config file. Change the values for name, email and password.
```json
"accessories": [
{
"accessory": "NeatoVacuumRobot",
"name": "YourRobot",
"email": "YourEmail",
"password": "YourPassword"
}
]
```
Feel free to leave any feedback [here](https://github.com/naofireblade/homebridge-neato/issues).
# Features
- Atart and pause cleaning
- Return to base
- Start and pause cleaning
- Return to dock\*
- Enable and disable schedule
- Enable and disable eco mode
- Get battery info
- Get dock info
- Periodic refresh of robot state
\* The robot needs to clean for some seconds before he knows where his dock is. After this time the switch to send him home will be automatically available.
**Hint:** To control the robot with your own commands just set up a scene with the name of your choice.
# Installation
1. Install homebridge using: `npm install -g homebridge`
2. Install this plugin using: `npm install -g homebridge-neato`
3. If you don't have a Neato account yet create one [here](https://www.neatorobotics.com/create-account/).
4. Update your configuration file. See the sample below.
### Configuration
Add the following information to your config file. Change the values for name, email and password.
The parameter **refresh** is optional (default 0=off) and adjusts in what interval (seconds) changes of the robot state will be pushed to homekit. The minimum refresh time is 60 seconds. You need this only when you set up rules based on the robot state and start him outside of homekit (e.g. with the Neato app).
```json
"accessories": [
{
"accessory": "NeatoVacuumRobot",
"name": "YourRobot",
"email": "YourEmail",
"password": "YourPassword",
"refresh": "0"
}
]
```
# Tested robots
- BotVac Connected Firmware 2.2.0
- BotVac Connected (Firmware 2.2.0)
- BotVac D5 Connected
if you have another connected neato robot, please [tell me](https://github.com/naofireblade/homebridge-neato/issues/new) your experience with this plugin.
If you have another connected neato robot, please [tell me](https://github.com/naofireblade/homebridge-neato/issues) about your experience with this plugin.

1
_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-cayman

283
index.js
View File

@@ -1,8 +1,18 @@
"use strict";
var inherits = require('util').inherits;
var botvac = require('node-botvac');
var inherits = require('util').inherits,
debug = require('debug')('homebridge-neato'),
botvac = require('node-botvac'),
var Service, Characteristic;
Service,
Characteristic,
vacuumRobotCleanService,
vacuumRobotGoToDockService,
vacuumRobotDockStateService,
vacuumRobotEcoService,
vacuumRobotScheduleService,
vacuumRobotBatteryService,
refresh,
timer
module.exports = function (homebridge) {
Service = homebridge.hap.Service;
@@ -17,8 +27,23 @@ function NeatoVacuumRobot(log, config) {
this.email = config['email'];
this.password = config['password'];
// default off
this.refresh = ('refresh' in config ? parseInt(config['refresh']) : 0);
// must be integer and positive
this.refresh = (typeof this.refresh !=='number' || (this.refresh%1)!==0 || this.refresh < 0) ? 0 : this.refresh;
// minimum 60s
this.refresh = (0 < this.refresh < 60) ? 60 : this.refresh;
this.vacuumRobotCleanService = new Service.Switch(this.name + " Clean", "clean");
this.vacuumRobotGoToDockService = new Service.Switch(this.name + " Go to Dock", "goToDock");
this.vacuumRobotDockStateService = new Service.OccupancySensor(this.name + " Dock", "dockState");
this.vacuumRobotEcoService = new Service.Switch(this.name + " Eco Mode", "eco");
this.vacuumRobotScheduleService = new Service.Switch(this.name + " Schedule", "schedule");
this.vacuumRobotBatteryService = new Service.BatteryService("Battery", "battery");
this.lastUpdate = null;
this.robot = null;
this.getStateTimer();
}
NeatoVacuumRobot.prototype = {
@@ -34,121 +59,147 @@ NeatoVacuumRobot.prototype = {
.setCharacteristic(Characteristic.Model, this.name)
.setCharacteristic(Characteristic.SerialNumber, this.serial);
this.vacuumRobotCleanService = new Service.Switch(this.name + " Clean", "clean");
this.vacuumRobotCleanService.getCharacteristic(Characteristic.On).on('set', this.clean.bind(this));
this.vacuumRobotCleanService.getCharacteristic(Characteristic.On).on('set', this.setClean.bind(this));
this.vacuumRobotCleanService.getCharacteristic(Characteristic.On).on('get', this.getClean.bind(this));
this.vacuumRobotDockService = new Service.Switch(this.name + " Dock", "dock");
this.vacuumRobotDockService.getCharacteristic(Characteristic.On).on('set', this.dock.bind(this));
this.vacuumRobotDockService.getCharacteristic(Characteristic.On).on('get', this.getDock.bind(this));
this.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).on('set', this.setGoToDock.bind(this));
this.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).on('get', this.getGoToDock.bind(this));
this.vacuumRobotEcoService = new Service.Switch(this.name + " Eco Mode", "eco");
this.vacuumRobotEcoService.getCharacteristic(Characteristic.On).on('set', this.eco.bind(this));
this.vacuumRobotDockStateService.getCharacteristic(Characteristic.OccupancyDetected).on('get', this.getDock.bind(this));
this.vacuumRobotEcoService.getCharacteristic(Characteristic.On).on('set', this.setEco.bind(this));
this.vacuumRobotEcoService.getCharacteristic(Characteristic.On).on('get', this.getEco.bind(this));
this.vacuumRobotScheduleService = new Service.Switch(this.name + " Schedule", "schedule");
this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('set', this.schedule.bind(this));
this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('set', this.setSchedule.bind(this));
this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('get', this.getSchedule.bind(this));
this.vacuumRobotBatteryService = new Service.BatteryService("Battery", "battery");
this.vacuumRobotBatteryService.getCharacteristic(Characteristic.BatteryLevel).on('get', this.getBatteryLevel.bind(this));
this.vacuumRobotBatteryService.getCharacteristic(Characteristic.ChargingState).on('get', this.getBatteryChargingState.bind(this));
return [this.informationService, this.vacuumRobotCleanService, this.vacuumRobotDockService, this.vacuumRobotEcoService,
return [this.informationService, this.vacuumRobotCleanService, this.vacuumRobotGoToDockService, this.vacuumRobotDockStateService, this.vacuumRobotEcoService,
this.vacuumRobotScheduleService, this.vacuumRobotBatteryService];
},
clean: function (on, callback) {
setClean: function (on, callback) {
let that = this;
if (on) {
this.getState(function (error, result) {
that.log(that.robot);
if (that.robot.canResume === true) {
that.log("Resume cleaning");
that.robot.resumeCleaning(function (error, result) {
that.log(result);
});
this.getStateAndRobot(function (error, result) {
if (on) {
if (that.robot.canResume || that.robot.canStart) {
// wait for robot to start and then disable the old timer and enable it again (with a shorter interval)
setTimeout(function() {
clearTimeout(that.timer);
that.getStateTimer();
}, 10000);
if (that.robot.canResume) {
debug("Resume cleaning");
that.robot.resumeCleaning(callback);
}
else {
debug("Start cleaning");
that.robot.startCleaning(that.robot.eco, 2, callback);
}
}
else {
that.log("Start cleaning");
that.robot.startCleaning(that.robot.eco, function (error, result) {
that.log(result);
debug("Already cleaning");
callback();
}
}
else {
if (that.robot.canPause) {
debug("Pause cleaning");
that.robot.pauseCleaning(callback);
}
else {
debug("Already stopped");
callback();
}
}
});
},
setGoToDock: function (on, callback) {
let that = this;
this.getStateAndRobot(function (error, result) {
if (on) {
if (that.robot.canPause) {
debug("Pause cleaning to go to dock");
that.robot.pauseCleaning(function (error, result) {
setTimeout(function() {
debug("Go to dock");
that.robot.sendToBase(callback);
}, 1000);
});
}
});
}
else {
this.log("Pause cleaning");
this.robot.pauseCleaning(false, function (error, result) {
that.log(result);
});
}
callback();
else if (that.robot.canGoToBase)
{
debug("Go to dock");
that.robot.sendToBase(callback);
}
else {
debug("Can't go to dock at the moment");
callback();
}
} else {
debug(that.robot);
callback();
}
});
},
dock: function (on, callback) {
let that = this;
that.log(that.robot);
if (on) {
that.log("Send to dock");
that.robot.sendToBase(false, function (error, result) {
that.log(result);
});
}
callback();
},
eco: function (on, callback) {
this.log(on ? "Enable eco mode" : "Disable eco mode");
setEco: function (on, callback) {
debug(on ? "Enable eco mode" : "Disable eco mode");
this.robot.eco = on;
callback();
},
schedule: function (on, callback) {
if (on) {
this.log("Enable schedule");
this.robot.enableSchedule(false, function (error, result) {
onsole.log(result);
});
}
else {
this.log("Disable schedule");
this.robot.disableSchedule(false, function (error, result) {
onsole.log(result);
});
}
callback();
setSchedule: function (on, callback) {
let that = this;
this.getStateAndRobot(function (error, result) {
if (on) {
debug("Enable schedule");
that.robot.enableSchedule(callback);
}
else {
debug("Disable schedule");
that.robot.disableSchedule(callback);
}
});
},
getClean: function(callback) {
let that = this;
this.getState(function (error, result) {
that.log("Is cleaning: " + that.robot.canPause);
this.getStateAndRobot(function (error, result) {
debug("Is cleaning: " + that.robot.canPause);
callback(false, that.robot.canPause);
});
},
getGoToDock: function(callback) {
let that = this;
this.getStateAndRobot(function (error, result) {
debug("Can go to dock: " + that.robot.dockHasBeenSeen);
callback(false, !that.robot.dockHasBeenSeen);
});
},
getDock: function(callback) {
let that = this;
this.getState(function (error, result) {
that.log("Can go to dock: " + that.robot.canGoToBase);
that.log("Is docked: " + that.robot.isDocked);
this.getStateAndRobot(function (error, result) {
debug("Is docked: " + that.robot.isDocked);
callback(false, that.robot.isDocked);
});
},
getEco: function(callback) {
let that = this;
this.getState(function (error, result) {
that.log("Eco mode: " + that.robot.eco);
callback(false, that.robot.eco);
});
// dont load eco here, because we cant save the eco state on the robot
callback(false, this.robot.eco);
},
getSchedule: function(callback) {
let that = this;
this.getState(function (error, result) {
that.log("Schedule: " + that.robot.isScheduleEnabled);
this.getStateAndRobot(function (error, result) {
debug("Schedule: " + that.robot.isScheduleEnabled);
callback(false, that.robot.isScheduleEnabled);
});
},
@@ -156,40 +207,40 @@ NeatoVacuumRobot.prototype = {
getBatteryLevel: function(callback) {
let that = this;
this.getState(function (error, result) {
that.log("Battery: " + that.robot.charge);
this.getStateAndRobot(function (error, result) {
debug("Battery: " + that.robot.charge);
callback(false, that.robot.charge);
});
},
getBatteryChargingState: function(callback) {
let that = this;
this.getState(function (error, result) {
that.log("Is charging: " + that.robot.isCharging);
this.getStateAndRobot(function (error, result) {
debug("Is charging: " + that.robot.isCharging);
callback(false, that.robot.isCharging);
});
},
getState: function(callback) {
getStateAndRobot: function(callback) {
let that = this;
if (this.robot === null)
{
this.getRobot(function (error, result) {
that._getState(callback);
that.getState(callback);
});
}
else {
that._getState(callback);
}
that.getState(callback);
}
},
_getState: function(callback) {
getState: function(callback) {
if (this.lastUpdate !== null && new Date() - this.lastUpdate < 2000) {
//this.log("Get state (cached)");
debug("Get state (cached)");
callback();
}
else {
//this.log("Get state (new)");
debug("Get state (new)");
let that = this;
this.robot.getState(function (error, result) {
that.lastUpdate = new Date();
@@ -198,23 +249,75 @@ NeatoVacuumRobot.prototype = {
}
},
getStateTimer: function() {
debug("Timer called");
let that = this;
this.getStateAndRobot(function (error, result) {
// only update these values if the state is different from the current one, otherwise we might accidentally start an action
if (that.vacuumRobotCleanService.getCharacteristic(Characteristic.On).value !== that.robot.canPause) {
that.vacuumRobotCleanService.setCharacteristic(Characteristic.On, that.robot.canPause);
}
if (that.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).value !== !that.robot.dockHasBeenSeen) {
that.vacuumRobotGoToDockService.setCharacteristic(Characteristic.On, !that.robot.dockHasBeenSeen);
}
if (that.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).value !== that.robot.isScheduleEnabled) {
that.vacuumRobotScheduleService.setCharacteristic(Characteristic.On, that.robot.isScheduleEnabled);
}
// no commands here, values can be updated without problems
that.vacuumRobotDockStateService.setCharacteristic(Characteristic.OccupancyDetected, that.robot.isDocked);
that.vacuumRobotBatteryService.setCharacteristic(Characteristic.BatteryLevel, that.robot.charge);
that.vacuumRobotBatteryService.setCharacteristic(Characteristic.ChargingState, that.robot.isCharging);
// dont update eco, because we cant write that value onto the robot and dont want it to be overwritten in our plugin
if (that.robot.canPause) {
debug("Short timer set: 30s");
that.timer = setTimeout(that.getStateTimer.bind(that), 30 * 1000);
}
else if (that.refresh != 0) {
debug("Long timer set: " + that.refresh + "s");
that.timer = setTimeout(that.getStateTimer.bind(that), that.refresh * 1000);
}
else {
debug("Disabled timer");
}
});
},
getRobot: function(callback) {
//this.log("Get robot");
debug("Get robot");
let client = new botvac.Client();
let that = this;
client.authorize(this.email, this.password, false, function (error) {
if (error) {
that.log(error);
that.log.error("Can't log on to neato cloud. Please check your credentials.")
}
else {
client.getRobots(function (error, robots) {
if (error) {
that.log(error);
that.log.error("Successful login but can't connect to your neato robot.")
}
else {
that.robot = robots[0];
that.log("Found robot: " + that.robot.name);
callback();
if (robots.length === 0) {
that.log.error("Successful login but no robots associated with your account.")
}
else {
that.robot = robots[0];
that.log("Found robot: " + that.robot.name);
that.getState(function (error, result) {
debug(that.robot);
})
if (robots.length > 1){
that.log.warn("Found more then one robot in your account. This plugin currently just supports one. First one found will be used.")
}
callback();
}
}
});
}

View File

@@ -1,10 +1,12 @@
{
"name": "homebridge-neato",
"version": "0.1.0",
"description": "control your neato vacuum robot",
"version": "0.3.1",
"description": "A Neato vacuum robot plugin for homebridge.",
"license": "MIT",
"keywords": [
"homebridge-plugin"
"homebridge-plugin",
"neato",
"botvac"
],
"engines": {
"node": ">=0.12.0",
@@ -18,6 +20,7 @@
"url": "git://github.com/naofireblade/homebridge-neato.git"
},
"dependencies": {
"node-botvac": ">=0.1.4"
"node-botvac": ">=0.1.5",
"debug": "^2.2.0"
}
}