26 Commits

Author SHA1 Message Date
Arne
db8305bbee Fixed homebridge startup failed when robot does not support zone cleaning 2019-07-20 22:37:27 +02:00
Arne
f795781d2a Code cleanup 2019-07-20 22:27:16 +02:00
Arne
3cfde323a8 Added gitignore 2019-07-20 22:20:31 +02:00
Arne
4a97487400 Added contributor and bumped version to v0.6.0 2019-07-14 23:35:09 +02:00
Arne
5ea9dde49c Merge pull request #28 from az0uz/feature/zone_cleaning
Support for zone cleaning
2019-07-14 23:32:43 +02:00
Arne
8ef24c9d40 Update package.json 2019-07-10 10:27:11 +02:00
Antoine de Maleprade
979dc40ccf fixed uuid reset when restarting homebridge, removed logs 2019-05-06 20:11:04 -07:00
Antoine de Maleprade
7d824ee0b9 Working zone cleaning, implemented multiple accesories to have a room per switch in homekit 2019-04-28 17:35:49 -07:00
Antoine de Maleprade
b87e49e12f fix: zone services discovery 2019-04-27 22:52:49 -07:00
Antoine de Maleprade
afe7d690ef Added zone cleaning 2019-04-27 22:06:07 -07:00
Arne
442b91c347 Versionbump to v0.5.2 2019-04-09 12:46:08 +02:00
Arne
55020c005b Merge pull request #26 from btutal/master
Add Schema for Config
2019-04-09 12:43:31 +02:00
Berkay
7c72aabb25 Add Schema for Config
Add schema file for homebridge-config-ui-x and similar plugins
2019-04-08 22:26:30 +02:00
Arne
e1754e02ed Fixed console log in node-botvac 2018-11-17 13:27:33 +01:00
Arne
0b864eb229 Updated readme 2018-11-14 22:42:38 +01:00
Arne
1d1fa121a4 Updated readme 2018-11-14 18:24:52 +01:00
Arne
6c6bb1c204 Updated readme and changelog 2018-11-14 18:22:27 +01:00
Arne
577e62ea32 Improved refresh interval 2018-11-13 21:47:39 +01:00
Arne
fbec31602f NoGo Lines and options sync
ADDED
- NoGo lines button
- Extra care navigation button
- Syncing cleaning options from last run

CHANGED
- Goto dock button is now always off
- Better error handling
- More detailed debug messages
- Updated node-botvac dependency to 0.1.6

REMOVED
- Extra care navigation option parameter (is now a button)
2018-11-11 15:53:50 +01:00
naofireblade
42fa0399f8 Version 0.4.7
* Fixed an exception when no robot is associated with the account
2018-03-26 19:14:54 +02:00
Arne
0de549dd41 Merge pull request #12 from ghulands/fix_no_robots
Set an empty array if there are no robots on the account
2018-03-25 22:01:32 +02:00
Greg Hulands
7fef07df0a Set an empty array if there are no robots on the account 2018-03-24 22:54:12 -07:00
Arne
9e9dd80547 Added badges 2018-02-08 12:14:48 +01:00
Arne
b7ec93953e Updated list of supported robots 2018-01-11 08:01:18 +01:00
naofireblade
ad41c1679d Version 0.4.6
* Added error log while refreshing robot state
* Fixed a rare bug where the robot stops after some seconds of cleaning
2017-12-10 13:14:12 +01:00
naofireblade
89f6f233a5 Added errorlog while refreshing robot state 2017-10-15 19:34:02 +02:00
6 changed files with 647 additions and 321 deletions

75
.gitignore vendored Normal file
View File

@@ -0,0 +1,75 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/dist-e2e
/tmp
/out-tsc
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
*.iml
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/libpeerconnection.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
package-lock.json

View File

@@ -53,4 +53,41 @@
## 0.4.5
* Fixed compatibility with homebridge 0.4.23 (occupancy sensor not working)
* Fixed compatibility with homebridge 0.4.23 (occupancy sensor not working)
## 0.4.6
* Added error log while refreshing robot state
* Fixed a rare bug where the robot stops after some seconds of cleaning
## 0.4.7
* Fixed an exception when no robot is associated with the account
## 0.5.0
* Added noGo lines button
* Added extra care navigation button
* Added syncing cleaning options from last run
* Added option to disable background state update completely
* Changed goto dock button is now always off
* Changed error handling
* Changed debug messages
* Updated node-botvac dependency to 0.1.6
* Removed extra care navigation option parameter (is now a button)
## 0.5.1
* Updated node-botvac dependency to 0.1.7
## 0.5.2
* Added schema file for use with homebridge-config-ui-x
## 0.6.0
* Added support for zone cleaning
## 0.6.1
* Fixed homebridge startup failed when robot does not support zone cleaning

View File

@@ -1,24 +1,25 @@
# homebridge-neato
[![npm](https://img.shields.io/npm/v/homebridge-neato.svg?style=flat-square)](https://www.npmjs.com/package/homebridge-neato)
[![npm](https://img.shields.io/npm/dt/homebridge-neato.svg?style=flat-square)](https://www.npmjs.com/package/homebridge-neato)
[![GitHub last commit](https://img.shields.io/github/last-commit/naofireblade/homebridge-neato.svg?style=flat-square)](https://github.com/naofireblade/homebridge-neato)
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).
Feel free to leave any feedback [here](https://github.com/naofireblade/homebridge-neato/issues).
If you update from a previous version 0.3.x you have to adapt your config.
## Features
- Start and pause cleaning
- Return to dock\*
- Enable and disable schedule
- Enable and disable eco mode
- Return to dock
- Scheduling
- Eco mode
- Extra care navigation
- Nogo lines
- Zone cleaning
- Get battery info
- Get dock info
- Periodic refresh of robot state
- Support for multiple robots
- Extra care navigation
\* Available after some seconds of cleaning.
## Installation
@@ -45,11 +46,9 @@ Add the following information to your config file. Change the values for email a
### Advanced
The following config contains advanced optional settings that are off when not specified.
The following config contains advanced optional settings.
The parameter **refresh** sets 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).
The parameter **extraCareNavigation** sets if supporting models (currently Neato D3 and D5) should take extra care of your furniture while cleaning.
The parameter **refresh** sets an interval in seconds that is used to update the robot state in the background. This is only required for automations based on the robot state. The default value is `auto` which means that the update is automatically enabled while cleaning and disabled while not cleaning. You can set a value in seconds e.g. `120` to enable background updates even when the robot is not cleaning. You can also disable background updates completely by setting the value `0`. This might be required if you experience timeouts in the app because you have other home automation apps that are connected to your robot.
The parameter **disabled** accepts a list of switches/sensors that can be disabled in the neato homekit plugin (e.g. dock, dockstate, eco, schedule).
@@ -60,8 +59,7 @@ The parameter **disabled** accepts a list of switches/sensors that can be disabl
"email": "YourEmail",
"password": "YourPassword",
"refresh": "120",
"extraCareNavigation": true,
"disabled": ["dock", "dockstate", "eco"]
"disabled": ["dock", "dockstate", "eco", "nogolines", "extracare", "schedule"]
}
]
```
@@ -70,6 +68,13 @@ The parameter **disabled** accepts a list of switches/sensors that can be disabl
- BotVac Connected (Firmware 2.2.0)
- BotVac D3 Connected
- BotVac D5 Connected
- BotVac D5 Connected (Firmware 4.0.0, 4.3.0)
- BotVac D7 Connected
If you have another connected neato robot, please [tell me](https://github.com/naofireblade/homebridge-neato/issues) about your experience with this plugin.
The plugin should work with D4 and D6 as well. If you have connected neato robot, please [tell me](https://github.com/naofireblade/homebridge-neato/issues) about your experience with this plugin.
## Contributors
Many thanks go to
- [ghulands](https://github.com/ghulands) for finding and fixing a bug when no robot is associated with the neato account
- [Berkay](https://github.com/btutal) for adding the schema file to use the plugin with homebridge-config-ui-x
- [Antoine de Maleprade](https://github.com/az0uz) for adding the zone cleaning feature

23
config.schema.json Normal file
View File

@@ -0,0 +1,23 @@
{
"pluginAlias": "NeatoVacuumRobot",
"pluginType": "platform",
"headerDisplay": "For Advanced settings like Refresh time interval or Disabled switches/sensors. [Check Here](https://github.com/naofireblade/homebridge-neato#readme)",
"schema": {
"type": "object",
"properties": {
"email": {
"title": "email",
"type": "string",
"required": true,
"format": "email",
"description": "Your Email Address"
},
"password": {
"title": "password",
"type": "string",
"required": true,
"description": "Your Password"
}
}
}
}

770
index.js
View File

@@ -1,350 +1,520 @@
"use strict";
var inherits = require('util').inherits,
debug = require('debug')('homebridge-neato'),
botvac = require('node-botvac'),
debug = require('debug')('homebridge-neato'),
botvac = require('node-botvac'),
Service,
Characteristic
Service,
Characteristic;
module.exports = function (homebridge) {
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerPlatform("homebridge-neato", "NeatoVacuumRobot", NeatoVacuumRobotPlatform);
}
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerPlatform("homebridge-neato", "NeatoVacuumRobot", NeatoVacuumRobotPlatform);
};
function NeatoVacuumRobotPlatform(log, config) {
this.log = log;
this.serial = "1-3-3-7";
this.email = config['email'];
this.password = config['password'];
this.hiddenServices = ('disabled' in config ? config['disabled'] : '');
this.log = log;
this.serial = "1-3-3-7";
this.email = config['email'];
this.password = config['password'];
this.hiddenServices = ('disabled' in config ? config['disabled'] : '');
this.careNavigation = ('extraCareNavigation' in config && config['extraCareNavigation'] ? 2 : 1);
debug("Extra Care Navigation: " + this.careNavigation);
// 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 = (this.refresh > 0 && this.refresh < 60) ? 60 : this.refresh;
if ('refresh' in config && config['refresh'] !== 'auto') {
// parse config parameter
this.refresh = parseInt(config['refresh']);
// must be integer and positive
this.refresh = (typeof this.refresh !== 'number' || (this.refresh % 1) !== 0 || this.refresh < 0) ? 60 : this.refresh;
// minimum 60s to save some load on the neato servers
this.refresh = (this.refresh > 0 && this.refresh < 60) ? 60 : this.refresh;
}
// default auto
else {
this.refresh = 'auto';
}
debug("Refresh is set to: " + this.refresh);
}
NeatoVacuumRobotPlatform.prototype = {
accessories: function(callback) {
this.accessories = [];
accessories: function (callback) {
let accessories = [];
let that = this;
this.robots = this.getRobots(function () {
for (var i = 0; i < that.robots.length; i++) {
that.log("Found robot #" + (i+1) + ": " + that.robots[i].name);
var robotAccessory = new NeatoVacuumRobotAccessory(that.robots[i], that);
that.accessories.push(robotAccessory);
}
callback(that.accessories);
});
},
let that = this;
this.getRobots(function () {
for (let i = 0; i < that.robots.length; i++) {
that.log("Found robot #" + (i + 1) + " named \"" + that.robots[i].name + "\" with serial \"" + that.robots[i]._serial + "\"");
let robotAccessory = new NeatoVacuumRobotAccessory(that.robots[i], that);
accessories.push(robotAccessory);
that.robots[i].maps.forEach((map) => {
map.boundaries.forEach((boundary) => {
if (boundary.type === "polygon") {
accessories.push(new NeatoVacuumRobotAccessory(that.robots[i], that, boundary))
}
})
})
}
callback(accessories);
});
},
getRobots: function(callback) {
debug("Get all robots");
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.");
callback();
}
else {
client.getRobots(function (error, robots) {
if (error) {
that.log(error);
that.log.error("Successful login but can't connect to your neato robot.");
callback();
}
else {
if (robots.length === 0) {
that.log.error("Successful login but no robots associated with your account.");
callback();
}
else {
that.robots = robots;
callback();
}
}
});
}
});
}
}
getRobots: function (callback) {
debug("Loading your robots");
let client = new botvac.Client();
let that = this;
client.authorize(this.email, this.password, false, (error) => {
if (error) {
that.log(error);
that.log.error("Can't log on to neato cloud. Please check your credentials.");
callback();
} else {
client.getRobots((error, robots) => {
if (error) {
that.log(error);
that.log.error("Successful login but can't connect to your neato robot.");
callback();
} else {
if (robots.length === 0) {
that.log.error("Successful login but no robots associated with your account.");
that.robots = [];
callback();
} else {
debug("Found " + robots.length + " robots");
let updatedRobotCount = 0;
that.robots = robots;
that.robots.forEach((robot) => {
robot.getPersistentMaps((error, result) => {
if (error) {
that.log("Error updating persistent maps: " + error + ": " + result);
callback();
return;
}
robot.maps = result;
let processedMapCount = 0;
if (robot.maps.length === 0)
{
callback();
}
robot.maps.forEach((map) => {
robot.getMapBoundaries(map.id, (error, result) => {
if (error) {
this.log("error getting boundaries: " + error + ": " + result)
} else {
map.boundaries = result.boundaries;
}
processedMapCount++;
if (processedMapCount == robot.maps.length) {
updatedRobotCount++;
if (updatedRobotCount === that.robots.length) {
callback();
}
}
})
})
})
})
}
}
});
}
});
}
};
function NeatoVacuumRobotAccessory(robot, platform) {
this.platform = platform;
this.log = platform.log;
this.refresh = platform.refresh;
this.careNavigation = platform.careNavigation;
this.hiddenServices = platform.hiddenServices;
this.robot = robot;
this.name = robot.name;
this.lastUpdate = null;
function NeatoVacuumRobotAccessory(robot, platform, boundary) {
this.platform = platform;
this.boundary = boundary;
this.log = platform.log;
this.refresh = platform.refresh;
this.hiddenServices = platform.hiddenServices;
this.robot = robot;
if (!this.boundary) {
this.name = robot.name;
} else {
this.name = this.robot.name + ' - ' + this.boundary.name;
}
this.lastUpdate = null;
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.vacuumRobotBatteryService = new Service.BatteryService("Battery", "battery");
this.updateRobotTimer();
if (!this.boundary) {
this.vacuumRobotCleanService = new Service.Switch("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.vacuumRobotNoGoLinesService = new Service.Switch(this.name + " NoGo Lines", "noGoLines");
this.vacuumRobotExtraCareService = new Service.Switch(this.name + " Extra Care", "extraCare");
this.vacuumRobotScheduleService = new Service.Switch(this.name + " Schedule", "schedule");
} else {
const splitName = boundary.name.split(' ');
let serviceName = "Clean the " + boundary.name;
if (splitName.length >= 2 && splitName[splitName.length - 2].match(/[']s$/g)) {
serviceName = "Clean " + boundary.name;
}
this.vacuumRobotCleanBoundaryService =
new Service.Switch(serviceName, "cleanBoundary:" + boundary.id);
this.log("Adding zone cleaning for: " + boundary.name);
}
this.updateRobotTimer();
}
NeatoVacuumRobotAccessory.prototype = {
identify: function (callback) {
let that = this;
this.updateRobot(function() {
// hide serial and secret in log
let _serial = that.robot._serial;
let _secret = that.robot._secret;
that.robot._serial = "*****";
that.robot._secret = "*****";
that.log(that.robot);
that.robot._serial = _serial;
that.robot._secret = _secret;
callback();
});
},
identify: function (callback) {
let that = this;
this.updateRobot(function () {
// hide serial and secret in log
let _serial = that.robot._serial;
let _secret = that.robot._secret;
that.robot._serial = "*****";
that.robot._secret = "*****";
that.log(that.robot);
that.robot._serial = _serial;
that.robot._secret = _secret;
});
},
getServices: function () {
debug(this.robot._serial);
this.informationService = new Service.AccessoryInformation();
this.informationService
.setCharacteristic(Characteristic.Name, this.robot.name)
.setCharacteristic(Characteristic.Manufacturer, "Neato Robotics")
.setCharacteristic(Characteristic.Model, "Coming soon")
.setCharacteristic(Characteristic.SerialNumber, this.robot._serial);
getServices: function () {
this.informationService = new Service.AccessoryInformation();
this.informationService
.setCharacteristic(Characteristic.Manufacturer, "Neato Robotics")
.setCharacteristic(Characteristic.Model, "Coming soon")
.setCharacteristic(Characteristic.SerialNumber, this.robot._serial);
if (!this.boundary) {
this.informationService
.setCharacteristic(Characteristic.Name, this.robot.name)
} else {
this.informationService
.setCharacteristic(Characteristic.Name, this.robot.name + ' - ' + this.boundary.name)
}
this.vacuumRobotCleanService.getCharacteristic(Characteristic.On).on('set', this.setClean.bind(this));
this.vacuumRobotCleanService.getCharacteristic(Characteristic.On).on('get', this.getClean.bind(this));
this.vacuumRobotBatteryService.getCharacteristic(Characteristic.BatteryLevel).on('get', this.getBatteryLevel.bind(this));
this.vacuumRobotBatteryService.getCharacteristic(Characteristic.ChargingState).on('get', this.getBatteryChargingState.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.services = [this.informationService, this.vacuumRobotBatteryService];
this.vacuumRobotDockStateService.getCharacteristic(Characteristic.OccupancyDetected).on('get', this.getDock.bind(this));
if (!this.boundary) {
this.vacuumRobotCleanService.getCharacteristic(Characteristic.On).on('set', this.setClean.bind(this));
this.vacuumRobotCleanService.getCharacteristic(Characteristic.On).on('get', this.getClean.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.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).on('set', this.setGoToDock.bind(this));
this.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).on('get', this.getGoToDock.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.vacuumRobotDockStateService.getCharacteristic(Characteristic.OccupancyDetected).on('get', this.getDock.bind(this));
this.vacuumRobotBatteryService.getCharacteristic(Characteristic.BatteryLevel).on('get', this.getBatteryLevel.bind(this));
this.vacuumRobotBatteryService.getCharacteristic(Characteristic.ChargingState).on('get', this.getBatteryChargingState.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.services = [this.informationService, this.vacuumRobotCleanService, this.vacuumRobotBatteryService];
if (this.hiddenServices.indexOf('dock') === -1)
this.services.push(this.vacuumRobotGoToDockService);
if (this.hiddenServices.indexOf('dockstate') === -1)
this.services.push(this.vacuumRobotDockStateService);
if (this.hiddenServices.indexOf('eco') === -1)
this.services.push(this.vacuumRobotEcoService);
if (this.hiddenServices.indexOf('schedule') === -1)
this.services.push(this.vacuumRobotScheduleService);
this.vacuumRobotNoGoLinesService.getCharacteristic(Characteristic.On).on('set', this.setNoGoLines.bind(this));
this.vacuumRobotNoGoLinesService.getCharacteristic(Characteristic.On).on('get', this.getNoGoLines.bind(this));
return this.services;
},
this.vacuumRobotExtraCareService.getCharacteristic(Characteristic.On).on('set', this.setExtraCare.bind(this));
this.vacuumRobotExtraCareService.getCharacteristic(Characteristic.On).on('get', this.getExtraCare.bind(this));
setClean: function (on, callback) {
let that = this;
this.updateRobot(function (error, result) {
if (on) {
if (that.robot.canResume || that.robot.canStart) {
// wait for robot to start and then start a short timer to recognize when he can go to dock or is finished
setTimeout(function() {
clearTimeout(that.timer);
that.updateRobotTimer();
}, 10000);
this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('set', this.setSchedule.bind(this));
this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('get', this.getSchedule.bind(this));
if (that.robot.canResume) {
debug(that.name + ": Resume cleaning");
that.robot.resumeCleaning(callback);
}
else {
debug(that.name + ": Start cleaning (" + that.careNavigation + ")");
that.robot.startCleaning(that.robot.eco, that.careNavigation, callback);
}
}
else {
debug(that.name + ": Cant start, maybe already cleaning");
callback();
}
}
else {
if (that.robot.canPause) {
debug(that.name + ": Pause cleaning");
that.robot.pauseCleaning(callback);
}
else {
debug(that.name + ": Already stopped");
callback();
}
}
});
},
this.services.push(this.vacuumRobotCleanService);
setGoToDock: function (on, callback) {
let that = this;
this.updateRobot(function (error, result) {
if (on) {
if (that.robot.canPause) {
debug(that.name + ": Pause cleaning to go to dock");
that.robot.pauseCleaning(function (error, result) {
setTimeout(function() {
debug("Go to dock");
that.robot.sendToBase(callback);
}, 1000);
});
}
else if (that.robot.canGoToBase)
{
debug(that.name + ": Go to dock");
that.robot.sendToBase(callback);
}
else {
debug(that.name + ": Can't go to dock at the moment");
callback();
}
} else {
callback();
}
});
},
if (this.hiddenServices.indexOf('dock') === -1)
this.services.push(this.vacuumRobotGoToDockService);
if (this.hiddenServices.indexOf('dockstate') === -1)
this.services.push(this.vacuumRobotDockStateService);
if (this.hiddenServices.indexOf('eco') === -1)
this.services.push(this.vacuumRobotEcoService);
if (this.hiddenServices.indexOf('nogolines') === -1)
this.services.push(this.vacuumRobotNoGoLinesService);
if (this.hiddenServices.indexOf('extracare') === -1)
this.services.push(this.vacuumRobotExtraCareService);
if (this.hiddenServices.indexOf('schedule') === -1)
this.services.push(this.vacuumRobotScheduleService);
}
setEco: function (on, callback) {
debug(this.name + ": " + (on ? "Enable eco mode" : "Disable eco mode"));
this.robot.eco = on;
callback();
},
if (this.boundary) {
this.vacuumRobotCleanBoundaryService.getCharacteristic(Characteristic.On).on('set', (on, serviceCallback) => {
this.setCleanBoundary(this.boundary.id, on, serviceCallback)
});
this.vacuumRobotCleanBoundaryService.getCharacteristic(Characteristic.On).on('get', (serviceCallback) => {
this.getCleanBoundary(this.boundary.id, serviceCallback);
});
this.services.push(this.vacuumRobotCleanBoundaryService);
}
setSchedule: function (on, callback) {
let that = this;
this.updateRobot(function (error, result) {
if (on) {
debug(that.name + ": Enable schedule");
that.robot.enableSchedule(callback);
}
else {
debug(that.name + ": Disable schedule");
that.robot.disableSchedule(callback);
}
});
},
return this.services;
},
getClean: function(callback) {
let that = this;
this.updateRobot(function (error, result) {
debug(that.name + ": Is cleaning: " + that.robot.canPause);
callback(false, that.robot.canPause);
});
},
setCleanBoundary: function (boundaryId, on, callback) {
this.updateRobot((error, result) => {
if (on) {
if (this.robot.canStart) {
this.log("Starting to clean: " + boundaryId);
this.robot.startCleaningBoundary(this.eco, this.extraCare, boundaryId, (error, result) => {
if (error) {
this.log(error + ": " + JSON.stringify(result));
}
callback();
});
return
} else {
this.log("Error, robot is already cleaning");
}
} else {
if (this.robot.canPause) {
debug(this.name + ": Pause cleaning");
this.robot.pauseCleaning(callback);
return;
} else {
debug(this.name + ": Already stopped");
}
}
callback();
});
},
getGoToDock: function(callback) {
let that = this;
this.updateRobot(function (error, result) {
debug(that.name + ": Can go to dock: " + that.robot.dockHasBeenSeen);
callback(false, !that.robot.dockHasBeenSeen);
});
},
getCleanBoundary: function (boundaryId, callback) {
this.updateRobot((error, result) => {
callback(false, this.robot.canPause && (this.robot.cleaningBoundaryId == boundaryId));
});
},
getDock: function(callback) {
let that = this;
this.updateRobot(function (error, result) {
debug(that.name + ": Is docked: " + that.robot.isDocked);
callback(false, that.robot.isDocked ? 1 : 0);
});
},
setClean: function (on, callback) {
let that = this;
this.updateRobot(function (error, result) {
if (on) {
if (that.robot.canResume || that.robot.canStart) {
getEco: function(callback) {
// dont load eco here, because we cant save the eco state on the robot
callback(false, this.robot.eco);
},
// start extra update robot timer if refresh is set to "auto"
if (that.refresh === 'auto') {
setTimeout(function () {
clearTimeout(that.timer);
that.updateRobotTimer();
}, 60 * 1000);
}
getSchedule: function(callback) {
let that = this;
this.updateRobot(function (error, result) {
debug(that.name + ": Schedule: " + that.robot.isScheduleEnabled);
callback(false, that.robot.isScheduleEnabled);
});
},
if (that.robot.canResume) {
debug(that.name + ": Resume cleaning");
that.robot.resumeCleaning(callback);
} else {
let eco = that.vacuumRobotEcoService.getCharacteristic(Characteristic.On).value;
let extraCare = that.vacuumRobotExtraCareService.getCharacteristic(Characteristic.On).value;
let nogoLines = that.vacuumRobotNoGoLinesService.getCharacteristic(Characteristic.On).value;
debug(that.name + ": Start cleaning (eco: " + eco + ", extraCare: " + extraCare + ", nogoLines: " + nogoLines + ")");
that.robot.startCleaning(
eco,
extraCare ? 2 : 1,
nogoLines,
function (error, result) {
if (error) {
that.log.error(error + ": " + result);
callback(true);
} else {
callback();
}
});
}
} else {
debug(that.name + ": Cant start, maybe already cleaning");
callback();
}
} else {
if (that.robot.canPause) {
debug(that.name + ": Pause cleaning");
that.robot.pauseCleaning(callback);
} else {
debug(that.name + ": Already stopped");
callback();
}
}
});
},
setGoToDock: function (on, callback) {
let that = this;
this.updateRobot(function (error, result) {
if (on) {
if (that.robot.canPause) {
debug(that.name + ": Pause cleaning to go to dock");
that.robot.pauseCleaning(function (error, result) {
setTimeout(function () {
debug("Go to dock");
that.robot.sendToBase(callback);
}, 1000);
});
} else if (that.robot.canGoToBase) {
debug(that.name + ": Go to dock");
that.robot.sendToBase(callback);
} else {
that.log.warn(that.name + ": Can't go to dock at the moment");
callback();
}
} else {
callback();
}
});
},
setEco: function (on, callback) {
debug(this.name + ": " + (on ? "Enable eco mode" : "Disable eco mode"));
this.robot.eco = on;
callback();
},
setNoGoLines: function (on, callback) {
debug(this.name + ": " + (on ? "Enable nogo lines" : "Disable nogo lines"));
this.robot.noGoLines = on;
callback();
},
setExtraCare: function (on, callback) {
debug(this.name + ": " + (on ? "Enable extra care navigation" : "Disable extra care navigation"));
this.robot.navigationMode = on ? 2 : 1;
callback();
},
setSchedule: function (on, callback) {
let that = this;
this.updateRobot(function (error, result) {
if (on) {
debug(that.name + ": Enable schedule");
that.robot.enableSchedule(callback);
} else {
debug(that.name + ": Disable schedule");
that.robot.disableSchedule(callback);
}
});
},
getClean: function (callback) {
let that = this;
this.updateRobot(function (error, result) {
debug(that.name + ": Is cleaning: " + that.robot.canPause);
callback(false, that.robot.canPause);
});
},
getGoToDock: function (callback) {
callback(false, false);
},
getDock: function (callback) {
let that = this;
this.updateRobot(function () {
debug(that.name + ": Is docked: " + that.robot.isDocked);
callback(false, that.robot.isDocked ? 1 : 0);
});
},
getEco: function (callback) {
let that = this;
this.updateRobot(function () {
debug(that.name + ": Is eco: " + that.robot.eco);
callback(false, that.robot.eco);
});
},
getNoGoLines: function (callback) {
let that = this;
this.updateRobot(function () {
debug(that.name + ": Is nogo lines: " + that.robot.noGoLines);
callback(false, that.robot.noGoLines ? 1 : 0);
});
},
getExtraCare: function (callback) {
let that = this;
this.updateRobot(function () {
debug(that.name + ": Is extra care navigation: " + (that.robot.navigationMode == 2 ? true : false));
callback(false, that.robot.navigationMode == 2 ? 1 : 0);
});
},
getSchedule: function (callback) {
let that = this;
this.updateRobot(function () {
debug(that.name + ": Is schedule: " + that.robot.isScheduleEnabled);
callback(false, that.robot.isScheduleEnabled);
});
},
getBatteryLevel: function(callback) {
let that = this;
this.updateRobot(function (error, result) {
debug(that.name + ": Battery: " + that.robot.charge);
callback(false, that.robot.charge);
});
},
getBatteryLevel: function (callback) {
let that = this;
this.updateRobot(function () {
debug(that.name + ": Battery: " + that.robot.charge + "%");
callback(false, that.robot.charge);
});
},
getBatteryChargingState: function(callback) {
let that = this;
this.updateRobot(function (error, result) {
debug(that.name + ": Is charging: " + that.robot.isCharging);
callback(false, that.robot.isCharging);
});
},
getBatteryChargingState: function (callback) {
let that = this;
this.updateRobot(function () {
debug(that.name + ": Is charging: " + that.robot.isCharging);
callback(false, that.robot.isCharging);
});
},
updateRobot: function(callback) {
let that = this;
if (this.lastUpdate !== null && new Date() - this.lastUpdate < 2000) {
debug(this.name + ": Update (cached)");
callback();
}
else {
debug(this.name + ": Update (online)");
this.robot.getState(function (error, result) {
that.lastUpdate = new Date();
callback();
});
}
},
updateRobot: function (callback) {
let that = this;
if (this.lastUpdate !== null && new Date() - this.lastUpdate < 2000) {
callback();
} else {
debug(this.name + ": Updating robot state");
this.robot.getState(function (error, result) {
if (error) {
that.log.error(error + ": " + result);
}
that.lastUpdate = new Date();
callback();
});
}
},
updateRobotTimer: function() {
let that = this;
debug(this.name + ": Timer called");
this.updateRobot(function (error, result) {
updateRobotTimer: function () {
let that = this;
this.updateRobot((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 (!this.boundary) {
// 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);
}
// dock switch is on (dock not seen before) and dock has just been seen -> turn switch off
if (that.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).value == true && that.robot.dockHasBeenSeen) {
that.vacuumRobotGoToDockService.setCharacteristic(Characteristic.On, false);
}
if (that.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).value !== that.robot.isScheduleEnabled) {
that.vacuumRobotScheduleService.setCharacteristic(Characteristic.On, that.robot.isScheduleEnabled);
}
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 ? 1 : 0);
that.vacuumRobotBatteryService.setCharacteristic(Characteristic.BatteryLevel, that.robot.charge);
that.vacuumRobotBatteryService.setCharacteristic(Characteristic.ChargingState, that.robot.isCharging);
// no commands here, values can be updated without problems
that.vacuumRobotDockStateService.setCharacteristic(Characteristic.OccupancyDetected, that.robot.isDocked ? 1 : 0);
that.vacuumRobotEcoService.setCharacteristic(Characteristic.On, that.robot.eco);
that.vacuumRobotNoGoLinesService.setCharacteristic(Characteristic.On, that.robot.noGoLines);
that.vacuumRobotExtraCareService.setCharacteristic(Characteristic.On, that.robot.navigationMode == 2 ? true : false);
// dont update eco, because we cant write that value onto the robot and dont want it to be overwritten in our plugin
} else {
if (this.vacuumRobotCleanBoundaryService.getCharacteristic(Characteristic.On).value !== that.robot.canPause) {
this.vacuumRobotCleanBoundaryService.setCharacteristic(Characteristic.On, that.robot.canPause);
}
}
if (that.robot.canPause) {
debug(that.name + ": Timer set (cleaning): 30s");
that.timer = setTimeout(that.updateRobotTimer.bind(that), 30 * 1000);
}
else if (that.refresh != 0) {
debug(that.name + ": Timer set (user): " + that.refresh + "s");
that.timer = setTimeout(that.updateRobotTimer.bind(that), that.refresh * 1000);
}
else {
debug(that.name + ": Timer stopped");
}
});
},
}
that.vacuumRobotBatteryService.setCharacteristic(Characteristic.BatteryLevel, that.robot.charge);
that.vacuumRobotBatteryService.setCharacteristic(Characteristic.ChargingState, that.robot.isCharging);
// robot is currently cleaning, update if refresh is set to auto or a specific interval
if (that.robot.canPause && that.refresh !== 0) {
let refreshTime = that.refresh === 'auto' ? 60 : that.refresh;
debug("Updating state in background every " + refreshTime + " seconds while cleaning");
that.timer = setTimeout(that.updateRobotTimer.bind(that), refreshTime * 1000);
}
// robot is not cleaning, but a specific refresh interval is set
else if (that.refresh !== 'auto' && that.refresh !== 0) {
debug("Updating state in background every " + that.refresh + " seconds (user setting)");
that.timer = setTimeout(that.updateRobotTimer.bind(that), that.refresh * 1000);
} else {
debug("Updating state in background disabled");
}
});
},
};

View File

@@ -1,6 +1,6 @@
{
"name": "homebridge-neato",
"version": "0.4.5",
"version": "0.6.1",
"description": "A Neato vacuum robot plugin for homebridge.",
"license": "MIT",
"keywords": [
@@ -13,14 +13,30 @@
"homebridge": ">=0.2.0"
},
"author": {
"name": "Arne Blumentritt"
"name": "Arne Blumentritt",
"url2": "https://github.com/naofireblade"
},
"contributors": [
{
"name": "ghulands",
"url": "https://github.com/ghulands"
},
{
"name": "Berkay",
"url": "https://github.com/btutal"
},
{
"name": "Antoine de Maleprade",
"url": "https://github.com/az0uz"
}
],
"repository": {
"type": "git",
"url": "git://github.com/naofireblade/homebridge-neato.git"
},
"dependencies": {
"node-botvac": ">=0.1.5",
"debug": "^2.2.0"
"debug": "^2.2.0",
"node-botvac": ">=0.3.0",
"uuid": "^3.3.2"
}
}