11 Commits

Author SHA1 Message Date
Arne
1436e4b342 Fixed homebridge startup failed when robot does not support mapping 2019-07-31 10:05:59 +02:00
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
5 changed files with 582 additions and 370 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

@@ -82,4 +82,16 @@
## 0.5.2 ## 0.5.2
* Added schema file for use with homebridge-config-ui-x * 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
## 0.6.2
* Fixed homebridge startup failed when robot does not support mapping

View File

@@ -11,10 +11,11 @@ Feel free to leave any feedback [here](https://github.com/naofireblade/homebridg
- Start and pause cleaning - Start and pause cleaning
- Return to dock - Return to dock
- Toggle schedule - Scheduling
- Toggle eco mode - Eco mode
- Toggle extra care navigation - Extra care navigation
- Toggle nogo lines - Nogo lines
- Zone cleaning
- Get battery info - Get battery info
- Get dock info - Get dock info
- Periodic refresh of robot state - Periodic refresh of robot state
@@ -76,3 +77,4 @@ The plugin should work with D4 and D6 as well. If you have connected neato robot
Many thanks go to Many thanks go to
- [ghulands](https://github.com/ghulands) for finding and fixing a bug when no robot is associated with the neato account - [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 - [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

827
index.js
View File

@@ -1,416 +1,523 @@
"use strict"; "use strict";
var inherits = require('util').inherits, var inherits = require('util').inherits,
debug = require('debug')('homebridge-neato'), debug = require('debug')('homebridge-neato'),
botvac = require('node-botvac'), botvac = require('node-botvac'),
Service, Service,
Characteristic Characteristic;
module.exports = function (homebridge) { module.exports = function (homebridge) {
Service = homebridge.hap.Service; Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic; Characteristic = homebridge.hap.Characteristic;
homebridge.registerPlatform("homebridge-neato", "NeatoVacuumRobot", NeatoVacuumRobotPlatform); homebridge.registerPlatform("homebridge-neato", "NeatoVacuumRobot", NeatoVacuumRobotPlatform);
} };
function NeatoVacuumRobotPlatform(log, config) { function NeatoVacuumRobotPlatform(log, config) {
this.log = log; this.log = log;
this.serial = "1-3-3-7"; this.serial = "1-3-3-7";
this.email = config['email']; this.email = config['email'];
this.password = config['password']; this.password = config['password'];
this.hiddenServices = ('disabled' in config ? config['disabled'] : ''); this.hiddenServices = ('disabled' in config ? config['disabled'] : '');
if ('refresh' in config && config['refresh'] !== 'auto') { if ('refresh' in config && config['refresh'] !== 'auto') {
// parse config parameter // parse config parameter
this.refresh = parseInt(config['refresh']); this.refresh = parseInt(config['refresh']);
// must be integer and positive // must be integer and positive
this.refresh = (typeof this.refresh !== 'number' || (this.refresh % 1) !== 0 || this.refresh < 0) ? 60 : this.refresh; 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 // minimum 60s to save some load on the neato servers
this.refresh = (this.refresh > 0 && this.refresh < 60) ? 60 : this.refresh; this.refresh = (this.refresh > 0 && this.refresh < 60) ? 60 : this.refresh;
} }
// default auto // default auto
else { else {
this.refresh = 'auto'; this.refresh = 'auto';
} }
debug("Refresh is set to: " + this.refresh); debug("Refresh is set to: " + this.refresh);
} }
NeatoVacuumRobotPlatform.prototype = { NeatoVacuumRobotPlatform.prototype = {
accessories: function (callback) { accessories: function (callback) {
this.accessories = []; let accessories = [];
let that = this; let that = this;
this.robots = this.getRobots(function () { this.getRobots(function () {
for (var i = 0; i < that.robots.length; i++) { 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 + "\""); that.log("Found robot #" + (i + 1) + " named \"" + that.robots[i].name + "\" with serial \"" + that.robots[i]._serial + "\"");
var robotAccessory = new NeatoVacuumRobotAccessory(that.robots[i], that); let robotAccessory = new NeatoVacuumRobotAccessory(that.robots[i], that);
that.accessories.push(robotAccessory); accessories.push(robotAccessory);
} if (that.robots[i].maps && that.robots[i].maps.length > 0)
callback(that.accessories); {
}); 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) { getRobots: function (callback) {
debug("Loading your robots"); debug("Loading your robots");
let client = new botvac.Client(); let client = new botvac.Client();
let that = this; let that = this;
client.authorize(this.email, this.password, false, function (error) { client.authorize(this.email, this.password, false, (error) => {
if (error) { if (error) {
that.log(error); that.log(error);
that.log.error("Can't log on to neato cloud. Please check your credentials."); that.log.error("Can't log on to neato cloud. Please check your credentials.");
callback(); callback();
} } else {
else { client.getRobots((error, robots) => {
client.getRobots(function (error, robots) { if (error) {
if (error) { that.log(error);
that.log(error); that.log.error("Successful login but can't connect to your neato robot.");
that.log.error("Successful login but can't connect to your neato robot."); callback();
callback(); } else {
} if (robots.length === 0) {
else { that.log.error("Successful login but no robots associated with your account.");
if (robots.length === 0) { that.robots = [];
that.log.error("Successful login but no robots associated with your account."); callback();
that.robots = []; } else {
callback(); debug("Found " + robots.length + " robots");
} let updatedRobotCount = 0;
else { that.robots = robots;
debug("Found " + robots.length + " robots"); that.robots.forEach((robot) => {
that.robots = robots; robot.getPersistentMaps((error, result) => {
callback(); 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) { function NeatoVacuumRobotAccessory(robot, platform, boundary) {
this.platform = platform; this.platform = platform;
this.log = platform.log; this.boundary = boundary;
this.refresh = platform.refresh; this.log = platform.log;
this.hiddenServices = platform.hiddenServices; this.refresh = platform.refresh;
this.robot = robot; this.hiddenServices = platform.hiddenServices;
this.name = robot.name; this.robot = robot;
this.lastUpdate = null; 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.vacuumRobotBatteryService = new Service.BatteryService("Battery", "battery");
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");
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 = { NeatoVacuumRobotAccessory.prototype = {
identify: function (callback) { identify: function (callback) {
let that = this; let that = this;
this.updateRobot(function () { this.updateRobot(function () {
// hide serial and secret in log // hide serial and secret in log
let _serial = that.robot._serial; let _serial = that.robot._serial;
let _secret = that.robot._secret; let _secret = that.robot._secret;
that.robot._serial = "*****"; that.robot._serial = "*****";
that.robot._secret = "*****"; that.robot._secret = "*****";
that.log(that.robot); that.log(that.robot);
that.robot._serial = _serial; that.robot._serial = _serial;
that.robot._secret = _secret; that.robot._secret = _secret;
callback(); });
}); },
},
getServices: function () { getServices: function () {
this.informationService = new Service.AccessoryInformation(); this.informationService = new Service.AccessoryInformation();
this.informationService this.informationService
.setCharacteristic(Characteristic.Name, this.robot.name) .setCharacteristic(Characteristic.Manufacturer, "Neato Robotics")
.setCharacteristic(Characteristic.Manufacturer, "Neato Robotics") .setCharacteristic(Characteristic.Model, "Coming soon")
.setCharacteristic(Characteristic.Model, "Coming soon") .setCharacteristic(Characteristic.SerialNumber, this.robot._serial);
.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.vacuumRobotBatteryService.getCharacteristic(Characteristic.BatteryLevel).on('get', this.getBatteryLevel.bind(this));
this.vacuumRobotCleanService.getCharacteristic(Characteristic.On).on('get', this.getClean.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.services = [this.informationService, this.vacuumRobotBatteryService];
this.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).on('get', this.getGoToDock.bind(this));
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.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).on('set', this.setGoToDock.bind(this));
this.vacuumRobotEcoService.getCharacteristic(Characteristic.On).on('get', this.getEco.bind(this)); this.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).on('get', this.getGoToDock.bind(this));
this.vacuumRobotNoGoLinesService.getCharacteristic(Characteristic.On).on('set', this.setNoGoLines.bind(this)); this.vacuumRobotDockStateService.getCharacteristic(Characteristic.OccupancyDetected).on('get', this.getDock.bind(this));
this.vacuumRobotNoGoLinesService.getCharacteristic(Characteristic.On).on('get', this.getNoGoLines.bind(this));
this.vacuumRobotExtraCareService.getCharacteristic(Characteristic.On).on('set', this.setExtraCare.bind(this)); this.vacuumRobotEcoService.getCharacteristic(Characteristic.On).on('set', this.setEco.bind(this));
this.vacuumRobotExtraCareService.getCharacteristic(Characteristic.On).on('get', this.getExtraCare.bind(this)); this.vacuumRobotEcoService.getCharacteristic(Characteristic.On).on('get', this.getEco.bind(this));
this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('set', this.setSchedule.bind(this)); this.vacuumRobotNoGoLinesService.getCharacteristic(Characteristic.On).on('set', this.setNoGoLines.bind(this));
this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('get', this.getSchedule.bind(this)); this.vacuumRobotNoGoLinesService.getCharacteristic(Characteristic.On).on('get', this.getNoGoLines.bind(this));
this.vacuumRobotBatteryService.getCharacteristic(Characteristic.BatteryLevel).on('get', this.getBatteryLevel.bind(this)); this.vacuumRobotExtraCareService.getCharacteristic(Characteristic.On).on('set', this.setExtraCare.bind(this));
this.vacuumRobotBatteryService.getCharacteristic(Characteristic.ChargingState).on('get', this.getBatteryChargingState.bind(this)); this.vacuumRobotExtraCareService.getCharacteristic(Characteristic.On).on('get', this.getExtraCare.bind(this));
this.services = [this.informationService, this.vacuumRobotCleanService, this.vacuumRobotBatteryService]; this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('set', this.setSchedule.bind(this));
if (this.hiddenServices.indexOf('dock') === -1) this.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).on('get', this.getSchedule.bind(this));
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);
return this.services; this.services.push(this.vacuumRobotCleanService);
},
setClean: function (on, callback) { if (this.hiddenServices.indexOf('dock') === -1)
let that = this; this.services.push(this.vacuumRobotGoToDockService);
this.updateRobot(function (error, result) { if (this.hiddenServices.indexOf('dockstate') === -1)
if (on) { this.services.push(this.vacuumRobotDockStateService);
if (that.robot.canResume || that.robot.canStart) { 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);
}
// start extra update robot timer if refresh is set to "auto" if (this.boundary) {
if (that.refresh === 'auto') { this.vacuumRobotCleanBoundaryService.getCharacteristic(Characteristic.On).on('set', (on, serviceCallback) => {
setTimeout(function () { this.setCleanBoundary(this.boundary.id, on, serviceCallback)
clearTimeout(that.timer); });
that.updateRobotTimer(); this.vacuumRobotCleanBoundaryService.getCharacteristic(Characteristic.On).on('get', (serviceCallback) => {
}, 60 * 1000); this.getCleanBoundary(this.boundary.id, serviceCallback);
} });
this.services.push(this.vacuumRobotCleanBoundaryService);
}
if (that.robot.canResume) { return this.services;
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) { setCleanBoundary: function (boundaryId, on, callback) {
let that = this; this.updateRobot((error, result) => {
this.updateRobot(function (error, result) { if (on) {
if (on) { if (this.robot.canStart) {
if (that.robot.canPause) { this.log("Starting to clean: " + boundaryId);
debug(that.name + ": Pause cleaning to go to dock"); this.robot.startCleaningBoundary(this.eco, this.extraCare, boundaryId, (error, result) => {
that.robot.pauseCleaning(function (error, result) { if (error) {
setTimeout(function () { this.log(error + ": " + JSON.stringify(result));
debug("Go to dock"); }
that.robot.sendToBase(callback); callback();
}, 1000); });
}); return
} } else {
else if (that.robot.canGoToBase) { this.log("Error, robot is already cleaning");
debug(that.name + ": Go to dock"); }
that.robot.sendToBase(callback); } else {
} if (this.robot.canPause) {
else { debug(this.name + ": Pause cleaning");
that.log.warn(that.name + ": Can't go to dock at the moment"); this.robot.pauseCleaning(callback);
callback(); return;
} } else {
} else { debug(this.name + ": Already stopped");
callback(); }
} }
}); callback();
}, });
},
setEco: function (on, callback) { getCleanBoundary: function (boundaryId, callback) {
debug(this.name + ": " + (on ? "Enable eco mode" : "Disable eco mode")); this.updateRobot((error, result) => {
this.robot.eco = on; callback(false, this.robot.canPause && (this.robot.cleaningBoundaryId == boundaryId));
callback(); });
}, },
setNoGoLines: function (on, callback) { setClean: function (on, callback) {
debug(this.name + ": " + (on ? "Enable nogo lines" : "Disable nogo lines")); let that = this;
this.robot.noGoLines = on; this.updateRobot(function (error, result) {
callback(); if (on) {
}, if (that.robot.canResume || that.robot.canStart) {
setExtraCare: function (on, callback) { // start extra update robot timer if refresh is set to "auto"
debug(this.name + ": " + (on ? "Enable extra care navigation" : "Disable extra care navigation")); if (that.refresh === 'auto') {
this.robot.navigationMode = on ? 2 : 1; setTimeout(function () {
callback(); clearTimeout(that.timer);
}, that.updateRobotTimer();
}, 60 * 1000);
}
setSchedule: function (on, callback) { if (that.robot.canResume) {
let that = this; debug(that.name + ": Resume cleaning");
this.updateRobot(function (error, result) { that.robot.resumeCleaning(callback);
if (on) { } else {
debug(that.name + ": Enable schedule"); let eco = that.vacuumRobotEcoService.getCharacteristic(Characteristic.On).value;
that.robot.enableSchedule(callback); let extraCare = that.vacuumRobotExtraCareService.getCharacteristic(Characteristic.On).value;
} let nogoLines = that.vacuumRobotNoGoLinesService.getCharacteristic(Characteristic.On).value;
else { debug(that.name + ": Start cleaning (eco: " + eco + ", extraCare: " + extraCare + ", nogoLines: " + nogoLines + ")");
debug(that.name + ": Disable schedule"); that.robot.startCleaning(
that.robot.disableSchedule(callback); 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();
}
}
});
},
getClean: function (callback) { setGoToDock: function (on, callback) {
let that = this; let that = this;
this.updateRobot(function (error, result) { this.updateRobot(function (error, result) {
debug(that.name + ": Is cleaning: " + that.robot.canPause); if (on) {
callback(false, that.robot.canPause); 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();
}
});
},
getGoToDock: function (callback) { setEco: function (on, callback) {
callback(false, false); debug(this.name + ": " + (on ? "Enable eco mode" : "Disable eco mode"));
}, this.robot.eco = on;
callback();
},
getDock: function (callback) { setNoGoLines: function (on, callback) {
let that = this; debug(this.name + ": " + (on ? "Enable nogo lines" : "Disable nogo lines"));
this.updateRobot(function () { this.robot.noGoLines = on;
debug(that.name + ": Is docked: " + that.robot.isDocked); callback();
callback(false, that.robot.isDocked ? 1 : 0); },
});
},
getEco: function (callback) { setExtraCare: function (on, callback) {
let that = this; debug(this.name + ": " + (on ? "Enable extra care navigation" : "Disable extra care navigation"));
this.updateRobot(function () { this.robot.navigationMode = on ? 2 : 1;
debug(that.name + ": Is eco: " + that.robot.eco); callback();
callback(false, that.robot.eco); },
});
},
getNoGoLines: function (callback) { setSchedule: function (on, callback) {
let that = this; let that = this;
this.updateRobot(function () { this.updateRobot(function (error, result) {
debug(that.name + ": Is nogo lines: " + that.robot.noGoLines); if (on) {
callback(false, that.robot.noGoLines ? 1 : 0); debug(that.name + ": Enable schedule");
}); that.robot.enableSchedule(callback);
}, } else {
debug(that.name + ": Disable schedule");
that.robot.disableSchedule(callback);
}
});
},
getExtraCare: function (callback) { getClean: function (callback) {
let that = this; let that = this;
this.updateRobot(function () { this.updateRobot(function (error, result) {
debug(that.name + ": Is extra care navigation: " + (that.robot.navigationMode == 2 ? true : false)); debug(that.name + ": Is cleaning: " + that.robot.canPause);
callback(false, that.robot.navigationMode == 2 ? 1 : 0); callback(false, that.robot.canPause);
}); });
}, },
getSchedule: function (callback) { getGoToDock: function (callback) {
let that = this; callback(false, false);
this.updateRobot(function () { },
debug(that.name + ": Is schedule: " + that.robot.isScheduleEnabled);
callback(false, that.robot.isScheduleEnabled); 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) { getBatteryLevel: function (callback) {
let that = this; let that = this;
this.updateRobot(function () { this.updateRobot(function () {
debug(that.name + ": Battery: " + that.robot.charge + "%"); debug(that.name + ": Battery: " + that.robot.charge + "%");
callback(false, that.robot.charge); callback(false, that.robot.charge);
}); });
}, },
getBatteryChargingState: function (callback) { getBatteryChargingState: function (callback) {
let that = this; let that = this;
this.updateRobot(function () { this.updateRobot(function () {
debug(that.name + ": Is charging: " + that.robot.isCharging); debug(that.name + ": Is charging: " + that.robot.isCharging);
callback(false, that.robot.isCharging); callback(false, that.robot.isCharging);
}); });
}, },
updateRobot: function (callback) { updateRobot: function (callback) {
let that = this; let that = this;
if (this.lastUpdate !== null && new Date() - this.lastUpdate < 2000) { if (this.lastUpdate !== null && new Date() - this.lastUpdate < 2000) {
callback(); callback();
} } else {
else { debug(this.name + ": Updating robot state");
debug(this.name + ": Updating robot state"); this.robot.getState(function (error, result) {
this.robot.getState(function (error, result) { if (error) {
if (error) { that.log.error(error + ": " + result);
that.log.error(error + ": " + result); }
} that.lastUpdate = new Date();
that.lastUpdate = new Date(); callback();
callback(); });
}); }
} },
},
updateRobotTimer: function () { updateRobotTimer: function () {
let that = this; let that = this;
this.updateRobot(function (error, result) { 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 (!this.boundary) {
if (that.vacuumRobotCleanService.getCharacteristic(Characteristic.On).value !== that.robot.canPause) { // only update these values if the state is different from the current one, otherwise we might accidentally start an action
that.vacuumRobotCleanService.setCharacteristic(Characteristic.On, that.robot.canPause); if (that.vacuumRobotCleanService.getCharacteristic(Characteristic.On).value !== that.robot.canPause) {
} that.vacuumRobotCleanService.setCharacteristic(Characteristic.On, that.robot.canPause);
}
// dock switch is on (dock not seen before) and dock has just been seen -> turn switch off // 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) { if (that.vacuumRobotGoToDockService.getCharacteristic(Characteristic.On).value == true && that.robot.dockHasBeenSeen) {
that.vacuumRobotGoToDockService.setCharacteristic(Characteristic.On, false); that.vacuumRobotGoToDockService.setCharacteristic(Characteristic.On, false);
} }
if (that.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).value !== that.robot.isScheduleEnabled) { if (that.vacuumRobotScheduleService.getCharacteristic(Characteristic.On).value !== that.robot.isScheduleEnabled) {
that.vacuumRobotScheduleService.setCharacteristic(Characteristic.On, that.robot.isScheduleEnabled); that.vacuumRobotScheduleService.setCharacteristic(Characteristic.On, that.robot.isScheduleEnabled);
} }
// no commands here, values can be updated without problems // no commands here, values can be updated without problems
that.vacuumRobotDockStateService.setCharacteristic(Characteristic.OccupancyDetected, that.robot.isDocked ? 1 : 0); that.vacuumRobotDockStateService.setCharacteristic(Characteristic.OccupancyDetected, that.robot.isDocked ? 1 : 0);
that.vacuumRobotEcoService.setCharacteristic(Characteristic.On, that.robot.eco); that.vacuumRobotEcoService.setCharacteristic(Characteristic.On, that.robot.eco);
that.vacuumRobotNoGoLinesService.setCharacteristic(Characteristic.On, that.robot.noGoLines); that.vacuumRobotNoGoLinesService.setCharacteristic(Characteristic.On, that.robot.noGoLines);
that.vacuumRobotExtraCareService.setCharacteristic(Characteristic.On, that.robot.navigationMode == 2 ? true : false); that.vacuumRobotExtraCareService.setCharacteristic(Characteristic.On, that.robot.navigationMode == 2 ? true : false);
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 } else {
if (that.robot.canPause && that.refresh !== 0) { if (this.vacuumRobotCleanBoundaryService.getCharacteristic(Characteristic.On).value !== that.robot.canPause) {
let refreshTime = that.refresh === 'auto' ? 60 : that.refresh this.vacuumRobotCleanBoundaryService.setCharacteristic(Characteristic.On, that.robot.canPause);
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 that.vacuumRobotBatteryService.setCharacteristic(Characteristic.BatteryLevel, that.robot.charge);
else if (that.refresh !== 'auto' && that.refresh !== 0) { that.vacuumRobotBatteryService.setCharacteristic(Characteristic.ChargingState, that.robot.isCharging);
debug("Updating state in background every " + that.refresh + " seconds (user setting)");
that.timer = setTimeout(that.updateRobotTimer.bind(that), that.refresh * 1000); // robot is currently cleaning, update if refresh is set to auto or a specific interval
} if (that.robot.canPause && that.refresh !== 0) {
else { let refreshTime = that.refresh === 'auto' ? 60 : that.refresh;
debug("Updating state in background disabled"); 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", "name": "homebridge-neato",
"version": "0.5.2", "version": "0.6.2",
"description": "A Neato vacuum robot plugin for homebridge.", "description": "A Neato vacuum robot plugin for homebridge.",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
@@ -13,14 +13,30 @@
"homebridge": ">=0.2.0" "homebridge": ">=0.2.0"
}, },
"author": { "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": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/naofireblade/homebridge-neato.git" "url": "git://github.com/naofireblade/homebridge-neato.git"
}, },
"dependencies": { "dependencies": {
"node-botvac": ">=0.1.7", "debug": "^2.2.0",
"debug": "^2.2.0" "node-botvac": ">=0.3.0",
"uuid": "^3.3.2"
} }
} }