Improved handling of differences between robots in account and cache

Added debug output for all robots in account
This commit is contained in:
Arne Blumentritt 2021-05-05 10:29:55 +02:00
parent e53bb3d777
commit bc527a60ba
3 changed files with 125 additions and 37 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "homebridge-neato", "name": "homebridge-neato",
"displayName": "Homebridge Neato", "displayName": "Homebridge Neato",
"version": "1.0.0-beta.2", "version": "1.0.0-beta.3",
"description": "A Neato vacuum robot plugin for homebridge.", "description": "A Neato vacuum robot plugin for homebridge.",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [

45
src/api.ts Normal file
View File

@ -0,0 +1,45 @@
var axios = require('axios');
function request(url, payload, method, headers, callback) {
if (!url || url === '') {
if (typeof callback === 'function') callback('no url specified');
return;
}
var options = {
data: null,
method: method === 'GET' ? 'GET' : 'POST',
url: url,
headers: {
'Accept': 'application/vnd.neato.nucleo.v1'
}
};
if (options.method === 'POST') {
options.data = payload;
}
if (typeof headers === 'object') {
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
options.headers[header] = headers[header];
}
}
}
let res, err;
axios(options)
.then(function (response) {
res = response.data;
})
.catch(function (error) {
err = error;
})
.finally(function () {
// Callback needs to be called in finally block, see: https://github.com/Pmant/node-botvac/issues/15
if (typeof callback === 'function') callback(err, res);
});
}
exports.request = request;

View File

@ -3,6 +3,8 @@ import NeatoApi from "node-botvac";
import {PLATFORM_NAME, PLUGIN_NAME} from "./settings"; import {PLATFORM_NAME, PLUGIN_NAME} from "./settings";
import {NeatoVacuumRobotAccessory} from "./accessories/NeatoVacuumRobot"; import {NeatoVacuumRobotAccessory} from "./accessories/NeatoVacuumRobot";
const api = require("./api");
/** /**
* HomebridgePlatform * HomebridgePlatform
* This class is the main constructor for your plugin, this is where you should * This class is the main constructor for your plugin, this is where you should
@ -14,7 +16,7 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
public readonly Characteristic: typeof Characteristic = this.api.hap.Characteristic; public readonly Characteristic: typeof Characteristic = this.api.hap.Characteristic;
// this is used to track restored cached accessories // this is used to track restored cached accessories
public readonly robotAccessories: PlatformAccessory[] = []; public readonly cachedRobotAccessories: PlatformAccessory[] = [];
constructor( constructor(
public readonly log: Logger, public readonly log: Logger,
@ -33,13 +35,14 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
configureAccessory(accessory: PlatformAccessory) configureAccessory(accessory: PlatformAccessory)
{ {
// add the restored accessory to the accessories cache so we can track if it has already been registered // add the restored accessory to the accessories cache so we can track if it has already been registered
this.robotAccessories.push(accessory); this.cachedRobotAccessories.push(accessory);
} }
discoverRobots() discoverRobots()
{ {
const client = new NeatoApi.Client(); const client = new NeatoApi.Client();
try try
{ {
// Login // Login
@ -52,30 +55,77 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
return; return;
} }
// Debug robot request TODO: remove after beta
let that = this;
api.request(client._baseUrl + '/users/me/robots', null, 'GET', {Authorization: client._tokenType + client._token}, (function (error, result) {
result.forEach(r => {
r.serial = "xxx" + r.serial.length;
r.secret_key = "xxx" + r.secret_key.length;
r.mac_address = "xxx" + r.mac_address.length;
that.log.debug("Robot Request Result: " + JSON.stringify(r));
});
if (error)
{
that.log.debug("Robot Request Error: " + JSON.stringify(error));
}
}));
// Get all robots from account // Get all robots from account
client.getRobots((error, robots) => { client.getRobots((error, robots) => {
if (error) if (error)
{ {
this.log.error("Successful login but can't connect to your neato robot: " + error); this.log.error("Successful login but can't list your neato robots. Error: " + error);
// TODO retry after x min
return;
}
else if (robots.length === 0)
{
this.log.error("Successful login but no robots associated with your account.");
// TODO retry after x min // TODO retry after x min
return; return;
} }
// Neato robots in account
if (robots.length === 0)
{
this.log.error("Neato account has no robots. Did you add your robot here: https://neatorobotics.com/my-neato/ ?");
}
else
{
this.log.info("Neato account has " + robots.length + " robot" + (robots.length === 1 ? "" : "s")); this.log.info("Neato account has " + robots.length + " robot" + (robots.length === 1 ? "" : "s"));
}
for (const robot of robots) // Neato robots in cache
this.log.debug("Plugin Cache has " + this.cachedRobotAccessories.length + " robot" + (this.cachedRobotAccessories.length === 1 ? "" : "s"));
for (let cachedRobot of this.cachedRobotAccessories)
{ {
// Get additional information for the robot let accountRobot = robots.find(robot => this.api.hap.uuid.generate(robot._serial) === cachedRobot.UUID);
if (accountRobot)
{
this.log.debug("[" + cachedRobot.displayName + "] Cached robot found in Neato account.");
}
else
{
this.log.error("[" + cachedRobot.displayName + "] Cached robot not found in Neato account. Robot will now be removed from homebridge.");
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [cachedRobot]);
}
}
// Add / Update homebridge accessories with robot information from neato. This must be done for new and existing robots to reflect changes in the name, firmware, pluginconfig etc.
for (let robot of robots)
{
// Check if robot already exists as an accessory
const uuid = this.api.hap.uuid.generate(robot._serial);
const cachedRobot = this.cachedRobotAccessories.find(accessory => accessory.UUID === uuid);
if (cachedRobot)
{
this.log.debug("[" + robot.name + "] Updating meta information for robot in cache.");
}
else
{
this.log.debug("[" + robot.name + "] Getting meta information for new robot.");
}
robot.getState((error, state) => { robot.getState((error, state) => {
if (error) if (error)
{ {
this.log.error("Error getting robot meta information: " + error + ": " + state); this.log.error("[" + robot.name + "] Error getting meta information. Is the robot connected to the internet? Error: " + error + ". State: " + state);
} }
else else
{ {
@ -83,40 +133,33 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
{ {
robot.meta = state.meta; robot.meta = state.meta;
const uuid = this.api.hap.uuid.generate(robot._serial); // Update existing robot accessor
const existingAccessory = this.robotAccessories.find(accessory => accessory.UUID === uuid); if (cachedRobot)
// the accessory already exists
if (existingAccessory)
{ {
this.log.info("[" + robot.name + "] Robot loaded from cache");
// TODO update maps // TODO update maps
existingAccessory.context.robot = robot; cachedRobot.context.robot = robot;
this.api.updatePlatformAccessories([existingAccessory]); this.api.updatePlatformAccessories([cachedRobot]);
new NeatoVacuumRobotAccessory(this, cachedRobot, this.config);
new NeatoVacuumRobotAccessory(this, existingAccessory, this.config); this.log.info("[" + robot.name + "] Successfully loaded from cache");
} }
// Create new robot accessory
else else
{ {
this.log.info("[" + robot.name + "] Robot created");
const accessory = new this.api.platformAccessory(robot.name, uuid);
accessory.context.robot = robot;
new NeatoVacuumRobotAccessory(this, accessory, this.config);
// link the accessory to your platform
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
// TODO get maps // TODO get maps
const newRobot = new this.api.platformAccessory(robot.name, uuid);
newRobot.context.robot = robot;
new NeatoVacuumRobotAccessory(this, newRobot, this.config);
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [newRobot]);
this.log.info("[" + robot.name + "] Successfully created as new robot");
} }
} }
catch (error) catch (error)
{ {
this.log.error("Error creating robot accessory: " + robot.name); this.log.error("[" + robot.name + "] Creating accessory failed. Error: " + error);
this.log.error(error);
throw new this.api.hap.HapStatusError(this.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); throw new this.api.hap.HapStatusError(this.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
} }
} }
// // Get all maps for each robot // // Get all maps for each robot
@ -176,7 +219,7 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
} }
catch (error) catch (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); 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: " + error);
} }
} }
} }