homebridge-kobold-Homaassis.../index.js

286 lines
7.5 KiB
JavaScript
Raw Permalink Normal View History

"use strict";
2019-09-19 12:10:17 +02:00
let inherits = require('util').inherits,
2019-09-19 09:37:16 +02:00
debug = require('debug')('homebridge-neato'),
botvac = require('node-botvac'),
2019-09-19 09:37:16 +02:00
Service,
Characteristic;
2019-09-19 09:37:16 +02:00
module.exports = function (homebridge)
{
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerPlatform("homebridge-neato", "NeatoVacuumRobot", NeatoVacuumRobotPlatform);
2019-07-20 22:27:16 +02:00
};
2019-09-19 09:37:16 +02:00
function NeatoVacuumRobotPlatform(log, config)
{
this.log = log;
this.serial = "1-3-3-7";
this.email = config['email'];
this.password = config['password'];
this.hiddenServices = '';
this.hiddenServices = ('disabled' in config ? config['disabled'] : this.hiddenServices);
this.hiddenServices = ('hidden' in config ? config['hidden'] : this.hiddenServices);
2019-09-19 09:37:16 +02:00
// Array of real robots and associated robot accessories (incl rooms)
this.robots = [];
this.nextRoom = null;
2019-09-19 09:37:16 +02:00
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
if (this.refresh > 0 && this.refresh < 60)
{
this.log.warn("Minimum refresh time is 60 seconds to not overload the neato servers");
this.refresh = (this.refresh > 0 && this.refresh < 60) ? 60 : this.refresh;
}
2019-09-19 09:37:16 +02:00
}
// default auto
else
{
this.refresh = 'auto';
}
this.log("Refresh is set to: " + this.refresh + (this.refresh !== 'auto' ? ' seconds' : ''));
}
NeatoVacuumRobotPlatform.prototype = {
2019-09-19 09:37:16 +02:00
accessories: function (callback)
{
debug("Get robots");
2019-09-19 09:37:16 +02:00
let accessories = [];
this.boundaryNames = [];
this.getRobots(() =>
2019-09-19 09:37:16 +02:00
{
this.robots.forEach((robot, i) =>
2019-09-19 09:37:16 +02:00
{
this.log("Found robot #" + (i + 1) + " named \"" + robot.device.name + "\" with serial \"" + robot.device._serial.substring(0,9) + "XXXXXXXXXXXX\"");
// Start Update Intervall
this.updateRobotTimer(robot.device._serial);
let NeatoVacuumRobotAccessory = require('./accessories/neatoVacuumRobot')(Service, Characteristic);
2019-09-22 22:30:29 +02:00
let mainAccessory = new NeatoVacuumRobotAccessory(this, robot);
accessories.push(mainAccessory);
2019-09-19 11:25:36 +02:00
robot.mainAccessory = mainAccessory;
robot.roomAccessories = [];
2019-09-19 11:25:36 +02:00
// For testing purposes only
2019-09-26 09:08:35 +02:00
// robot.boundary = {name: "Testroom", id: "1"};
// let roomAccessory = new NeatoVacuumRobotAccessory(this, robot);
// accessories.push(roomAccessory);
// robot.roomAccessories.push(roomAccessory);
2019-09-26 09:08:35 +02:00
if (robot.device.maps)
{
robot.device.maps.forEach((map) =>
{
if (map.boundaries)
{
map.boundaries.forEach((boundary) =>
{
if (boundary.type === "polygon")
{
robot.boundary = boundary;
let roomAccessory = new NeatoVacuumRobotAccessory(this, robot);
accessories.push(roomAccessory);
robot.roomAccessories.push(roomAccessory);
}
})
}
})
}
});
2019-09-19 09:37:16 +02:00
callback(accessories);
});
},
getRobots: function (callback)
{
debug("Loading your robots");
let client = new botvac.Client();
// Login
2019-09-19 09:37:16 +02:00
client.authorize(this.email, this.password, false, (error) =>
{
if (error)
{
this.log.error("Can't log on to neato cloud. Please check your internet connection and your credentials. Try again later if the neato servers have issues: " + error);
2019-09-19 09:37:16 +02:00
callback();
}
else
{
// Get all robots
2019-09-19 09:37:16 +02:00
client.getRobots((error, robots) =>
{
if (error)
{
this.log.error("Successful login but can't connect to your neato robot: " + error);
callback();
}
else if (robots.length === 0)
{
this.log.error("Successful login but no robots associated with your account.");
this.robots = [];
2019-09-19 09:37:16 +02:00
callback();
}
else
{
debug("Found " + robots.length + " robots");
let loadedRobots = 0;
robots.forEach((robot) =>
2019-09-19 09:37:16 +02:00
{
// Get all maps for each robot
robot.getPersistentMaps((error, result) =>
2019-09-19 09:37:16 +02:00
{
if (error)
{
this.log.error("Error updating persistent maps: " + error + ": " + result);
callback();
}
// Robot has no maps
else if (result.length === 0)
{
robot.maps = [];
this.saveRobot(robot, loadedRobots, robots.length, callback);
}
// Robot has maps
else
2019-09-19 09:37:16 +02:00
{
robot.maps = result;
let loadedMaps = 0;
2019-09-19 09:37:16 +02:00
robot.maps.forEach((map) =>
{
// Save zones in each map
2019-09-19 09:37:16 +02:00
robot.getMapBoundaries(map.id, (error, result) =>
{
if (error)
{
this.log.error("Error getting boundaries: " + error + ": " + result)
2019-09-19 09:37:16 +02:00
}
else
{
map.boundaries = result.boundaries;
}
loadedMaps++;
// Robot is completely requested if zones for all maps are loaded
if (loadedMaps === robot.maps.length)
2019-09-19 09:37:16 +02:00
{
this.saveRobot(robot, loadedRobots, robots.length, callback);
2019-09-19 09:37:16 +02:00
}
})
});
}
});
});
2019-09-19 09:37:16 +02:00
}
});
}
});
},
saveRobot: function (robot, loadedRobots, size, callback)
{
// Get additional information for the robot
robot.getState((error, result) =>
{
if (error)
{
this.log.error("Error getting robot meta information: " + error + ": " + result);
callback();
}
else
{
// Store the robot with his information, maps and zones
this.robots.push({device: robot, meta: result.meta, availableServices: result.availableServices});
loadedRobots++;
// Initial request is complete if all robots are loaded.
if (loadedRobots === size)
{
callback();
}
}
});
},
updateRobot: function (serial, callback)
{
let robot = this.getRobot(serial);
// Data is up to date
if (typeof (robot.lastUpdate) !== 'undefined' && new Date() - robot.lastUpdate < 2000)
{
callback();
}
else
{
debug(robot.device.name + ": ++ Updating robot state");
robot.lastUpdate = new Date();
robot.device.getState((error, result) =>
{
if (error)
{
this.log.error("Cannot update robot. Check if robot is online. " + error);
}
callback();
});
}
},
getRobot(serial)
{
let result;
this.robots.forEach(function (robot)
{
if (robot.device._serial === serial)
{
result = robot;
}
});
return result;
},
updateRobotTimer: function (serial)
{
this.updateRobot(serial, () =>
{
let robot = this.getRobot(serial);
// Clear any other overlapping timers for this robot
clearTimeout(robot.timer);
// Tell all accessories of this robot (mainAccessory and roomAccessories) that updated robot data is available
robot.mainAccessory.updated();
robot.roomAccessories.forEach(accessory =>
{
accessory.updated();
});
// Periodic refresh interval set in config
if (this.refresh !== 'auto' && this.refresh !== 0)
{
debug(robot.device.name + ": ++ Next background update in " + this.refresh + " seconds");
robot.timer = setTimeout(this.updateRobotTimer.bind(this), this.refresh * 1000, serial);
}
// Auto refresh set in config
else if (this.refresh === 'auto' && robot.device.canPause)
{
debug(robot.device.name + ": ++ Next background update in 60 seconds while cleaning (auto mode)");
robot.timer = setTimeout(this.updateRobotTimer.bind(this), 60 * 1000, serial);
}
// No refresh
else
{
debug(robot.device.name + ": ++ Stopped background updates");
}
});
},
2019-09-19 11:25:36 +02:00
};