2021-05-08 19:33:37 +02:00
import { API , Characteristic , DynamicPlatformPlugin , Logger , PlatformAccessory , PlatformConfig , Service } from "homebridge" ;
import KoboldApi from "node-kobold-control" ;
import { PLATFORM_NAME , PLUGIN_NAME } from "./settings" ;
import { KoboldVacuumRobotAccessory } from "./accessories/koboldVacuumRobot" ;
/ * *
* HomebridgePlatform
* This class is the main constructor for your plugin , this is where you should
* parse the user config and discover / register accessories with Homebridge .
* /
export class HomebridgeKoboldPlatform implements DynamicPlatformPlugin
{
public readonly Service : typeof Service = this . api . hap . Service ;
public readonly Characteristic : typeof Characteristic = this . api . hap . Characteristic ;
// this is used to track restored cached accessories
public readonly cachedRobotAccessories : PlatformAccessory [ ] = [ ] ;
constructor (
public readonly log : Logger ,
public readonly config : PlatformConfig ,
public readonly api : API )
{
this . api . on ( "didFinishLaunching" , ( ) = > {
this . discoverRobots ( ) ;
} ) ;
}
/ * *
* This function is invoked when homebridge restores cached accessories from disk at startup .
* It should be used to setup event handlers for characteristics and update respective values .
* /
configureAccessory ( accessory : PlatformAccessory )
{
// add the restored accessory to the accessories cache so we can track if it has already been registered
this . cachedRobotAccessories . push ( accessory ) ;
}
discoverRobots ( )
{
const client = new KoboldApi . Client ( ) ;
this . log . debug ( "blubb" ) ;
try
{
// Login
2021-05-08 20:02:25 +02:00
client . authorize ( ( this . config ) [ "token" ] , ( error ) = > {
2021-05-08 19:33:37 +02:00
if ( error )
{
2021-05-08 20:02:25 +02:00
this . log . error ( "Cannot connect to Vorwerk server. No new robots will be found and existing robots will be unresponsive. Retrying in 5 minutes." ) ;
2021-05-08 19:33:37 +02:00
this . log . error ( "Error: " + error ) ;
setTimeout ( ( ) = > {
this . discoverRobots ( ) ;
} , 5 * 60 * 1000 ) ;
return ;
}
// Get all robots from account
client . getRobots ( ( error , robots ) = > {
if ( error )
{
2021-05-08 20:02:25 +02:00
this . log . error ( "Successful login but can't list the robots in your Vorwerk robots. Retrying in 5 minutes." ) ;
2021-05-08 19:33:37 +02:00
this . log . error ( "Error: " + error ) ;
setTimeout ( ( ) = > {
this . discoverRobots ( ) ;
} , 5 * 60 * 1000 ) ;
return ;
}
2021-05-08 20:02:25 +02:00
// Vorwerk robots in account
2021-05-08 19:33:37 +02:00
if ( robots . length === 0 )
{
2021-05-08 20:02:25 +02:00
this . log . error ( "Vorwerk account has no robots." ) ;
2021-05-08 19:33:37 +02:00
}
else
{
2021-05-08 20:02:25 +02:00
this . log . info ( "Vorwerk account has " + robots . length + " robot" + ( robots . length === 1 ? "" : "s" ) ) ;
2021-05-08 19:33:37 +02:00
}
2021-05-08 20:02:25 +02:00
// Vorwerk robots in cache
2021-05-08 19:33:37 +02:00
this . log . debug ( "Plugin Cache has " + this . cachedRobotAccessories . length + " robot" + ( this . cachedRobotAccessories . length === 1 ? "" : "s" ) ) ;
for ( let cachedRobot of this . cachedRobotAccessories )
{
let accountRobot = robots . find ( robot = > this . api . hap . uuid . generate ( robot . _serial ) === cachedRobot . UUID ) ;
if ( accountRobot )
{
2021-05-08 20:02:25 +02:00
this . log . debug ( "[" + cachedRobot . displayName + "] Cached robot found in Vorwerk account." ) ;
2021-05-08 19:33:37 +02:00
}
else
{
2021-05-08 20:02:25 +02:00
this . log . error ( "[" + cachedRobot . displayName + "] Cached robot not found in Vorwerk account. Robot will now be removed from homebridge." ) ;
2021-05-08 19:33:37 +02:00
this . api . unregisterPlatformAccessories ( PLUGIN_NAME , PLATFORM_NAME , [ cachedRobot ] ) ;
}
}
2021-05-08 20:02:25 +02:00
// Add / Update homebridge accessories with robot information from Vorwerk. This must be done for new and existing robots to reflect changes in the name, firmware, pluginconfig etc.
2021-05-08 19:33:37 +02:00
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 + "] Connecting to cached robot and updating information." ) ;
}
else
{
this . log . debug ( "[" + robot . name + "] Connecting to new robot and updating information." ) ;
}
robot . getState ( ( error , state ) = > {
if ( error )
{
this . log . error ( "[" + robot . name + "] Cannot connect to robot. Is the robot connected to the internet? Retrying in 5 minutes." ) ;
this . log . error ( "Error: " + error ) ;
setTimeout ( ( ) = > {
this . discoverRobots ( ) ;
} , 5 * 60 * 1000 ) ;
}
else
{
try
{
robot . meta = state . meta ;
// Update existing robot accessor
if ( cachedRobot )
{
// TODO update maps
cachedRobot . context . robot = robot ;
this . api . updatePlatformAccessories ( [ cachedRobot ] ) ;
new KoboldVacuumRobotAccessory ( this , cachedRobot , this . config ) ;
this . log . info ( "[" + robot . name + "] Successfully loaded robot from cache" ) ;
}
// Create new robot accessory
else
{
// TODO get maps
const newRobot = new this . api . platformAccessory ( robot . name , uuid ) ;
newRobot . context . robot = robot ;
new KoboldVacuumRobotAccessory ( this , newRobot , this . config ) ;
this . api . registerPlatformAccessories ( PLUGIN_NAME , PLATFORM_NAME , [ newRobot ] ) ;
this . log . info ( "[" + robot . name + "] Successfully created as new robot" ) ;
}
}
catch ( error )
{
this . log . error ( "[" + robot . name + "] Creating accessory failed. Error: " + error ) ;
throw new this . api . hap . HapStatusError ( this . api . hap . HAPStatus . SERVICE_COMMUNICATION_FAILURE ) ;
}
}
// // Get all maps for each robot
// robot.getPersistentMaps((error, maps) => {
// if (error)
// {
// this.log.error("Error updating persistent maps: " + error + ": " + maps);
// callback();
// }
// // Robot has no maps
// else if (maps.length === 0)
// {
// robot.maps = [];
// this.robotAccessories.push({device: robot, meta: state.meta, availableServices: state.availableServices});
// loadedRobots++;
// if (loadedRobots === robots.length)
// {
// callback();
// }
// }
// // Robot has maps
// else
// {
// robot.maps = maps;
// let loadedMaps = 0;
// robot.maps.forEach((map) => {
// // Save zones in each map
// robot.getMapBoundaries(map.id, (error, result) => {
// if (error)
// {
// this.log.error("Error getting boundaries: " + error + ": " + result)
// }
// else
// {
// map.boundaries = result.boundaries;
// }
// loadedMaps++;
//
// // Robot is completely requested if zones for all maps are loaded
// if (loadedMaps === robot.maps.length)
// {
// this.robotAccessories.push({device: robot, meta: state.meta, availableServices: state.availableServices});
// loadedRobots++;
// if (loadedRobots === robots.length)
// {
// callback();
// }
// }
// })
// });
// }
// });
} ) ;
}
} ) ;
} ) ;
}
catch ( error )
{
2021-05-08 20:02:25 +02:00
this . log . error ( "Can't log on to Vorwerk cloud. Please check your internet connection and your credentials. Try again later if the neato servers have issues. Error: " + error ) ;
2021-05-08 19:33:37 +02:00
}
}
}