WIP dynamic platform

This commit is contained in:
Arne Blumentritt 2021-05-03 15:39:10 +02:00
parent e0bd97ee5d
commit b13885bea7
4 changed files with 300 additions and 149 deletions

View File

@ -62,8 +62,14 @@
"ts-node": "^9.1.1", "ts-node": "^9.1.1",
"typescript": "^4.2.2" "typescript": "^4.2.2"
}, },
"funding": { "funding": [
"type" : "buymeacoffee", {
"url" : "https://buymeacoffee.com/naofireblade" "type": "buymeacoffee",
} "url": "https://buymeacoffee.com/naofireblade"
},
{
"type": "paypal",
"url": "https://paypal.me/ArneBlumentritt"
}
]
} }

View File

@ -1,7 +1,6 @@
import {CharacteristicValue, Logger, PlatformAccessory, PlatformConfig, Service} from 'homebridge'; import {CharacteristicValue, Logger, PlatformAccessory, PlatformAccessoryEvent, PlatformConfig, Service} from 'homebridge';
import {HomebridgeNeatoPlatform} from '../homebridgeNeatoPlatform'; import {HomebridgeNeatoPlatform} from '../homebridgeNeatoPlatform';
import {Options} from '../models/options';
const debug = require('debug')('my-app:my-module');
/** /**
* Platform Accessory * Platform Accessory
@ -13,15 +12,16 @@ export class NeatoVacuumRobotAccessory
private robot: any; private robot: any;
private log: Logger; private log: Logger;
private readonly refresh: any; private readonly refresh: any;
private spotClean: boolean; private isSpotCleaning: boolean;
private readonly options: Options;
private options: any; private timer: any;
private batteryService: Service; private batteryService: Service;
private cleanService: Service; private cleanService: Service;
private findMeService: Service; private findMeService: Service;
private goToDockService: Service; private goToDockService: Service;
private dockStateService: Service; private dockStateService: Service;
private binFullService: Service;
private ecoService: Service; private ecoService: Service;
private noGoLinesService: Service; private noGoLinesService: Service;
private extraCareService: Service; private extraCareService: Service;
@ -39,8 +39,9 @@ export class NeatoVacuumRobotAccessory
private readonly isNew: Boolean, private readonly isNew: Boolean,
private readonly config: PlatformConfig) private readonly config: PlatformConfig)
{ {
this.robot = accessory.context.robot;
this.log = platform.log; this.log = platform.log;
this.robot = accessory.context.robot;
this.options = accessory.context.options || new Options();
if ('refresh' in this.config && this.config['refresh'] !== 'auto') if ('refresh' in this.config && this.config['refresh'] !== 'auto')
{ {
@ -59,10 +60,9 @@ export class NeatoVacuumRobotAccessory
{ {
this.refresh = 'auto'; this.refresh = 'auto';
} }
this.log.debug(this.robot.name + " ## Refresh set to: " + this.refresh); this.debug(DebugType.STATUS, "Background update interval is: " + this.refresh);
this.spotClean = false; this.isSpotCleaning = false;
this.options = {};
// set accessory information // set accessory information
this.accessory.getService(this.platform.Service.AccessoryInformation)! this.accessory.getService(this.platform.Service.AccessoryInformation)!
@ -72,61 +72,102 @@ export class NeatoVacuumRobotAccessory
.setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.robot.meta.firmware) .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.robot.meta.firmware)
.setCharacteristic(this.platform.Characteristic.Name, this.robot.name); .setCharacteristic(this.platform.Characteristic.Name, this.robot.name);
this.accessory.on(PlatformAccessoryEvent.IDENTIFY, () => {
this.robot.getState((error, result) => {
this.log.info(this.robot.name + " Identified");
if (error)
{
this.debug(DebugType.INFO, JSON.stringify("Error: " + error));
}
this.debug(DebugType.INFO, "Status: " + JSON.stringify(result));
});
});
this.batteryService = this.accessory.getService(this.platform.Service.Battery) || this.accessory.addService(this.platform.Service.Battery) this.batteryService = this.accessory.getService(this.platform.Service.Battery) || this.accessory.addService(this.platform.Service.Battery)
this.cleanService = this.getSwitchService(this.robot.name + " Clean"); this.cleanService = this.getSwitchService(this.robot.name + " Clean House");
this.findMeService = this.getSwitchService(this.robot.name + " Find Me"); this.spotCleanService = this.getSwitchService(this.robot.name + " Clean Spot");
this.goToDockService = this.getSwitchService(this.robot.name + " Go to Dock"); this.goToDockService = this.getSwitchService(this.robot.name + " Go to Dock");
this.dockStateService = this.getOccupancyService(this.robot.name + " Docked") this.dockStateService = this.getOccupancyService(this.robot.name + " Docked")
this.ecoService = this.getSwitchService(this.robot.name + " Eco Mode"); this.binFullService = this.getOccupancyService(this.robot.name + " Bin Full")
this.noGoLinesService = this.getSwitchService(this.robot.name + " NoGo Lines"); this.findMeService = this.getSwitchService(this.robot.name + " Find Me");
this.extraCareService = this.getSwitchService(this.robot.name + " Extra Care"); this.scheduleService = this.getSwitchService(this.robot.name + " Option: Schedule");
this.scheduleService = this.getSwitchService(this.robot.name + " Schedule"); this.ecoService = this.getSwitchService(this.robot.name + " Option: Eco Mode");
this.spotCleanService = this.getSwitchService(this.robot.name + " Clean Spot"); this.noGoLinesService = this.getSwitchService(this.robot.name + " Option: NoGo Lines");
this.extraCareService = this.getSwitchService(this.robot.name + " Option: Extra Care");
this.cleanService.getCharacteristic(this.platform.Characteristic.On) this.cleanService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setClean.bind(this)) .onSet(this.setCleanHouse.bind(this))
.onGet(this.getClean.bind(this)); .onGet(this.getCleanHouse.bind(this));
this.findMeService.getCharacteristic(this.platform.Characteristic.On) this.spotCleanService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setFindMe.bind(this)) .onSet(this.setSpotClean.bind(this))
.onGet(this.getFindMe.bind(this)); .onGet(this.getSpotClean.bind(this));
this.goToDockService.getCharacteristic(this.platform.Characteristic.On) this.goToDockService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setGoToDock.bind(this)) .onSet(this.setGoToDock.bind(this))
.onGet(this.getGoToDock.bind(this)); .onGet(this.getGoToDock.bind(this));
this.dockStateService.getCharacteristic(this.platform.Characteristic.On) this.dockStateService.getCharacteristic(this.platform.Characteristic.OccupancyDetected)
.onGet(this.getDocked.bind(this));
this.dockStateService.getCharacteristic(this.platform.Characteristic.OccupancyDetected)
.onGet(this.getBinFull.bind(this));
this.findMeService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setFindMe.bind(this))
.onGet(this.getFindMe.bind(this)); .onGet(this.getFindMe.bind(this));
this.scheduleService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setSchedule.bind(this))
.onGet(this.getSchedule.bind(this));
this.ecoService.getCharacteristic(this.platform.Characteristic.On) this.ecoService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setEco.bind(this)) .onSet(this.setEco.bind(this))
.onGet(this.getEco.bind(this)); .onGet(this.getEco.bind(this));
this.noGoLinesService.getCharacteristic(this.platform.Characteristic.On) this.noGoLinesService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setFindMe.bind(this)) .onSet(this.setNoGoLines.bind(this))
.onGet(this.getFindMe.bind(this)); .onGet(this.getNoGoLines.bind(this));
this.extraCareService.getCharacteristic(this.platform.Characteristic.On) this.extraCareService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setFindMe.bind(this)) .onSet(this.setExtraCare.bind(this))
.onGet(this.getFindMe.bind(this)); .onGet(this.getExtraCare.bind(this));
this.scheduleService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setFindMe.bind(this)) this.updateRobotPeriodically().then(() => {
.onGet(this.getFindMe.bind(this)); if (!accessory.context.options)
this.spotCleanService.getCharacteristic(this.platform.Characteristic.On) {
.onSet(this.setSpotClean.bind(this)) this.options.eco = this.robot.eco;
.onGet(this.getSpotClean.bind(this)); this.options.noGoLines = this.robot.noGoLines;
this.options.extraCare = this.robot.navigationMode == 2;
this.updateRobotPeriodically().then(r => this.log.debug(this.robot.name + " ## Periodic update started with interval: " + this.refresh)); this.debug(DebugType.INFO, "Options initially set to eco: " + this.options.eco + ", noGoLines: " + this.options.noGoLines + ", extraCare: " + this.options.extraCare);
accessory.context.options = this.options;
}
else
{
this.debug(DebugType.INFO, "Options loaded from cache eco: " + this.options.eco + ", noGoLines: " + this.options.noGoLines + ", extraCare: " + this.options.extraCare);
}
});
} }
getSwitchService(servicename: string) private getSwitchService(servicename: string)
{ {
return this.accessory.getService(servicename) || this.accessory.addService(this.platform.Service.Switch, servicename, servicename) return this.accessory.getService(servicename) || this.accessory.addService(this.platform.Service.Switch, servicename, servicename)
} }
getOccupancyService(servicename: string) private getOccupancyService(servicename: string)
{ {
return this.accessory.getService(servicename) || this.accessory.addService(this.platform.Service.OccupancySensor, servicename, servicename) return this.accessory.getService(servicename) || this.accessory.addService(this.platform.Service.OccupancySensor, servicename, servicename)
} }
async setClean(on: CharacteristicValue) async getCleanHouse(): Promise<CharacteristicValue>
{ {
// TODO debug(this.robot.name + ": " + (on ? "Enabled ".brightGreen : "Disabled".red) + " Clean " + (this.boundary ? JSON.stringify(this.boundary) : '')); try
{
await this.updateRobot();
return this.robot.canPause && !this.isSpotCleaning;
}
catch (error)
{
this.log.error("Cannot get cleaning status: " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
async setCleanHouse(on: CharacteristicValue)
{
this.debug(DebugType.STATUS, "Set CLEAN HOUSE: " + on);
try try
{ {
await this.updateRobot(); await this.updateRobot();
@ -137,20 +178,18 @@ export class NeatoVacuumRobotAccessory
// Resume cleaning // Resume cleaning
if (this.robot.canResume) if (this.robot.canResume)
{ {
debug(this.robot.name + " => Resume cleaning"); this.debug(DebugType.ACTION, "Resume cleaning");
await this.robot.resumeCleaning(); await this.robot.resumeCleaning();
return;
} }
// Start cleaning // Start cleaning
else if (this.robot.canStart) else if (this.robot.canStart)
{ {
await this.clean(CleanType.ALL) await this.clean(CleanType.HOUSE)
} }
// Cannot start // Cannot start
else else
{ {
this.log.debug(this.robot.name + ": Cannot start, maybe already cleaning (expected)"); this.debug(DebugType.INFO, "Cannot start, maybe already cleaning (expected)");
return;
} }
} }
// Stop // Stop
@ -158,12 +197,12 @@ export class NeatoVacuumRobotAccessory
{ {
if (this.robot.canPause) if (this.robot.canPause)
{ {
this.log.debug(this.robot.name + " => Pause cleaning"); this.debug(DebugType.ACTION, "Pause cleaning");
await this.robot.pauseCleaning(); await this.robot.pauseCleaning();
} }
else else
{ {
this.log.debug(this.robot.name + ": Already paused"); this.debug(DebugType.INFO, "Already paused");
} }
} }
} }
@ -174,33 +213,39 @@ export class NeatoVacuumRobotAccessory
} }
} }
async getClean(): Promise<CharacteristicValue> async getSpotClean(): Promise<CharacteristicValue>
{ {
try try
{ {
await this.updateRobot(); await this.updateRobot();
let on = this.robot.canPause && !this.spotClean; return this.robot.canPause && this.isSpotCleaning;
this.log.debug(this.robot.name + " ## Clean is: " + on);
return on;
} }
catch (error) catch (error)
{ {
this.log.warn("Cannot get cleaning status: " + error); this.log.error("Cannot get spot cleaning status: " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
} }
} }
async getSpotClean(): Promise<CharacteristicValue>
{
await this.updateRobot();
let on = this.robot.canPause && this.spotClean;
this.log.debug(this.robot.name + " ## Spot Clean is: " + on);
return on;
}
async setSpotClean(on: CharacteristicValue) async setSpotClean(on: CharacteristicValue)
{ {
await this.clean(CleanType.SPOT) this.debug(DebugType.STATUS, "Set SPOT CLEAN: " + on);
try
{
if (on)
{
await this.clean(CleanType.SPOT)
}
else
{
// TODO stop/pause
}
}
catch (error)
{
this.log.error("Error setting spot cleaning to: " + on + ". " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
} }
getGoToDock() getGoToDock()
@ -210,10 +255,11 @@ export class NeatoVacuumRobotAccessory
async setGoToDock(on: CharacteristicValue) async setGoToDock(on: CharacteristicValue)
{ {
this.debug(DebugType.STATUS, "Set GO TO DOCK: " + on);
if (on) if (on)
{ {
await this.updateRobot(); await this.updateRobot();
setTimeout(() => { setTimeout(() => {
this.goToDockService.updateCharacteristic(this.platform.Characteristic.On, false); this.goToDockService.updateCharacteristic(this.platform.Characteristic.On, false);
}, 1000); }, 1000);
@ -222,7 +268,7 @@ export class NeatoVacuumRobotAccessory
{ {
if (this.robot.canPause) if (this.robot.canPause)
{ {
this.log.debug(this.robot.name + " => Pause cleaning to go to dock"); this.debug(DebugType.ACTION, "Pause cleaning to go to dock");
await this.robot.pauseCleaning(); await this.robot.pauseCleaning();
setTimeout(async () => { setTimeout(async () => {
await this.robot.sendToBase(); await this.robot.sendToBase();
@ -230,7 +276,7 @@ export class NeatoVacuumRobotAccessory
} }
else if (this.robot.canGoToBase) else if (this.robot.canGoToBase)
{ {
this.log.debug(this.robot.name + " => Go to dock"); this.debug(DebugType.ACTION, "Going to dock");
await this.robot.sendToBase(); await this.robot.sendToBase();
} }
else else
@ -240,23 +286,106 @@ export class NeatoVacuumRobotAccessory
} }
catch (error) catch (error)
{ {
this.log.warn("Error setting go to dock to: " + on + ". " + error); this.log.error("Error setting go to dock to: " + on + ". " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
} }
} }
} }
async getDocked(): Promise<CharacteristicValue>
{
try
{
await this.updateRobot();
return this.robot.isDocked;
}
catch (error)
{
this.log.error("Cannot get docked status: " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
async getBinFull(): Promise<CharacteristicValue>
{
try
{
await this.updateRobot();
return this.robot.isBinFull;
}
catch (error)
{
this.log.error("Cannot get bin full status: " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
async getSchedule(): Promise<CharacteristicValue>
{
try
{
await this.updateRobot();
return this.robot.isScheduleEnabled;
}
catch (error)
{
this.log.error("Cannot get schedule status: " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
async setSchedule(on: CharacteristicValue)
{
this.debug(DebugType.STATUS, "Set SCHEDULE: " + on);
try
{
if (on)
{
await this.robot.enableSchedule();
}
else
{
await this.robot.disableSchedule();
}
}
catch (error)
{
this.log.error("Error setting schedule to: " + on + ". " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
getEco() getEco()
{ {
let on = this.options.eco; return this.options.eco;
this.log.debug(this.robot.name + " ## Eco is: " + on);
return on;
} }
setEco(on: CharacteristicValue) setEco(on: CharacteristicValue)
{ {
this.options.eco = on; this.debug(DebugType.STATUS, "Set ECO: " + on);
this.log.debug(this.robot.name + " ## Option eco set to: " + on); this.options.eco = <boolean>on;
}
getExtraCare()
{
return this.options.extraCare;
}
setExtraCare(on: CharacteristicValue)
{
this.debug(DebugType.STATUS, "Set EXTRA CARE: " + on);
this.options.extraCare = <boolean>on;
}
getNoGoLines()
{
return this.options.noGoLines;
}
setNoGoLines(on: CharacteristicValue)
{
this.debug(DebugType.STATUS, "Set NOGO LINES: " + on);
this.options.noGoLines = <boolean>on;
} }
getFindMe() getFindMe()
@ -266,9 +395,10 @@ export class NeatoVacuumRobotAccessory
async setFindMe(on: CharacteristicValue) async setFindMe(on: CharacteristicValue)
{ {
this.debug(DebugType.STATUS, "Set FIND ME: " + on);
if (on) if (on)
{ {
this.log.debug(this.robot.name + " => Find me") this.debug(DebugType.ACTION, "Find me");
setTimeout(() => { setTimeout(() => {
this.findMeService.updateCharacteristic(this.platform.Characteristic.On, false); this.findMeService.updateCharacteristic(this.platform.Characteristic.On, false);
}, 1000); }, 1000);
@ -279,7 +409,7 @@ export class NeatoVacuumRobotAccessory
} }
catch (error) catch (error)
{ {
this.log.warn(this.robot.name + " ## Cannot start find me. " + error); this.log.error(this.robot.name + " ## Cannot start find me. " + error);
throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
} }
} }
@ -287,27 +417,22 @@ export class NeatoVacuumRobotAccessory
async clean(cleanType: CleanType) async clean(cleanType: CleanType)
{ {
// Start automatic update while cleaning // Enable shorter background update while cleaning
if (this.refresh === 'auto') setTimeout(() => {
{ this.updateRobotPeriodically();
setTimeout(() => { }, 60 * 1000);
this.updateRobotPeriodically();
}, 60 * 1000);
}
this.log.info("Start cleaning with options type: " + cleanType + ", eco: " + this.options.eco + ", noGoLines: " + this.options.noGoLines + ", extraCare: " + this.options.extraCare); this.log.info("[" + this.robot.name + "] > Start cleaning with options type: " + CleanType[cleanType] + ", eco: " + this.options.eco + ", noGoLines: " + this.options.noGoLines + ", extraCare: " + this.options.extraCare);
try try
{ {
switch (cleanType) switch (cleanType)
{ {
case CleanType.ALL: case CleanType.HOUSE:
await this.robot.startCleaning(this.options.eco, this.options.extraCare ? 2 : 1, this.options.noGoLines); await this.robot.startCleaning(this.options.eco, this.options.extraCare ? 2 : 1, this.options.noGoLines);
this.spotClean = false;
break; break;
case CleanType.SPOT: case CleanType.SPOT:
await this.robot.startSpotCleaning(this.options.eco, this.options.spot.width, this.options.spot.height, this.options.spot.repeat, this.options.extraCare ? 2 : 1); await this.robot.startSpotCleaning(this.options.eco, this.options.spot.width, this.options.spot.height, this.options.spot.repeat, this.options.extraCare ? 2 : 1);
this.spotClean = true;
break; break;
} }
} }
@ -322,20 +447,16 @@ export class NeatoVacuumRobotAccessory
// Data is outdated // Data is outdated
if (typeof (this.robot.lastUpdate) === 'undefined' || new Date().getTime() - this.robot.lastUpdate > 2000) if (typeof (this.robot.lastUpdate) === 'undefined' || new Date().getTime() - this.robot.lastUpdate > 2000)
{ {
debug(this.robot.name + ": ++ Updating robot state");
this.robot.lastUpdate = new Date().getTime(); this.robot.lastUpdate = new Date().getTime();
try try
{ {
await this.robot.getState(); this.robot.getState((error, result) => {
this.isSpotCleaning = result != null && result.action == 2;
// Set options initially by api // Battery
if (typeof this.options.eco == 'undefined') this.batteryService.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.robot.charge);
{ this.batteryService.updateCharacteristic(this.platform.Characteristic.ChargingState, this.robot.isCharging);
this.options.eco = this.robot.eco; });
this.options.noGoLines = this.robot.noGoLines;
this.options.extraCare = this.robot.navigationMode == 2;
this.log.debug("Options initially set to eco: " + this.options.eco + ", noGoLines: " + this.options.noGoLines + ", extraCare: " + this.options.extraCare);
}
} }
catch (error) catch (error)
{ {
@ -347,11 +468,13 @@ export class NeatoVacuumRobotAccessory
async updateRobotPeriodically() async updateRobotPeriodically()
{ {
this.debug(DebugType.INFO, "Performing background update")
await this.updateRobot() await this.updateRobot()
await this.updateCharacteristics(); await this.updateCharacteristics();
// Clear any other overlapping timers for this robot // Clear any other overlapping timers for this robot
clearTimeout(this.robot.timer); clearTimeout(this.timer);
// Tell all accessories of this robot (mainAccessory and roomAccessories) that updated robot data is available // Tell all accessories of this robot (mainAccessory and roomAccessories) that updated robot data is available
// this.robot.mainAccessory.updated(); // this.robot.mainAccessory.updated();
@ -360,35 +483,25 @@ export class NeatoVacuumRobotAccessory
// }); // });
// Periodic refresh interval set in config // Periodic refresh interval set in config
if (this.refresh !== 'auto' && this.refresh !== 0) let interval;
if (this.robot.canPause)
{ {
this.log.debug(this.robot.name + " ## Next background update in " + this.refresh + " seconds"); interval = 1;
this.robot.timer = setTimeout(this.updateRobotPeriodically.bind(this), this.refresh * 1000);
} }
// Auto refresh set in config (cleaning)
else if (this.refresh === 'auto' && !this.robot.canPause)
{
this.log.debug(this.robot.name + " ## Next background update in 30 minutes (auto mode)");
this.robot.timer = setTimeout(this.updateRobotPeriodically.bind(this), 30 * 60 * 1000);
}
// Auto refresh set in config (cleaning)
else if (this.refresh === 'auto' && this.robot.canPause)
{
this.log.debug(this.robot.name + " ## Next background update in 60 seconds while cleaning (auto mode)");
this.robot.timer = setTimeout(this.updateRobotPeriodically.bind(this), 60 * 1000);
}
// No refresh
else else
{ {
debug(this.robot.name + " ## Stopped background updates"); interval = this.refresh == "auto" ? 30 : this.refresh;
} }
this.debug(DebugType.INFO, "Background update done. Next update in " + interval + " minute(s)" + ((this.robot.canPause) ? ", robot is currently cleaning" : ""));
this.timer = setTimeout(this.updateRobotPeriodically.bind(this), interval * 60 * 1000);
} }
async updateCharacteristics() async updateCharacteristics()
{ {
// Update Switches // Update Switches
// Clean // Clean
this.cleanService.updateCharacteristic(this.platform.Characteristic.On, await this.getClean()); this.cleanService.updateCharacteristic(this.platform.Characteristic.On, await this.getCleanHouse());
// Spot Clean // Spot Clean
this.spotCleanService.updateCharacteristic(this.platform.Characteristic.On, await this.getSpotClean()); this.spotCleanService.updateCharacteristic(this.platform.Characteristic.On, await this.getSpotClean());
@ -396,17 +509,51 @@ export class NeatoVacuumRobotAccessory
// Go To Dock // Go To Dock
this.goToDockService.updateCharacteristic(this.platform.Characteristic.On, await this.getGoToDock()); this.goToDockService.updateCharacteristic(this.platform.Characteristic.On, await this.getGoToDock());
// // Schedule // Docked
// this.scheduleService.updateCharacteristic(this.platform.Characteristic.On, await this.getSchedule()); this.dockStateService.updateCharacteristic(this.platform.Characteristic.OccupancyDetected, await this.getDocked());
// Bin full
this.binFullService.updateCharacteristic(this.platform.Characteristic.OccupancyDetected, await this.getBinFull());
// Battery // Schedule
this.batteryService.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.robot.charge); this.scheduleService.updateCharacteristic(this.platform.Characteristic.On, await this.getSchedule());
this.batteryService.updateCharacteristic(this.platform.Characteristic.ChargingState, this.robot.isCharging);
// Eco
this.ecoService.updateCharacteristic(this.platform.Characteristic.On, await this.getEco());
// Extra Care
this.extraCareService.updateCharacteristic(this.platform.Characteristic.On, await this.getExtraCare());
// NoGo Lines
this.noGoLinesService.updateCharacteristic(this.platform.Characteristic.On, await this.getNoGoLines());
}
debug(debugType: DebugType, message: String)
{
switch (debugType)
{
case DebugType.ACTION:
this.log.debug("[" + this.robot.name + "] > " + message);
break;
case DebugType.STATUS:
this.log.debug("[" + this.robot.name + "] " + message);
break;
case DebugType.INFO:
this.log.debug("[" + this.robot.name + "] " + message);
break;
}
} }
} }
enum CleanType enum CleanType
{ {
ALL, HOUSE,
SPOT SPOT
} }
enum DebugType
{
ACTION,
STATUS,
INFO
}

View File

@ -1,11 +1,8 @@
import {API, Characteristic, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service} from 'homebridge'; import {API, Characteristic, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service} from 'homebridge';
import Debug from "debug";
import NeatoApi from "node-botvac"; 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 debug = Debug("homebridge-neato");
/** /**
* 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
@ -24,10 +21,7 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
public readonly config: PlatformConfig, public readonly config: PlatformConfig,
public readonly api: API) public readonly api: API)
{ {
this.log.debug('Finished initializing platform:', this.config.platform);
this.api.on('didFinishLaunching', () => { this.api.on('didFinishLaunching', () => {
log.debug('Executed didFinishLaunching callback');
this.discoverRobots(); this.discoverRobots();
}); });
} }
@ -38,15 +32,12 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
*/ */
configureAccessory(accessory: PlatformAccessory) configureAccessory(accessory: PlatformAccessory)
{ {
this.log.info('Loading accessory from cache:', accessory.displayName);
// 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.robotAccessories.push(accessory);
} }
discoverRobots() discoverRobots()
{ {
debug("Discovering new robots");
let client = new NeatoApi.Client(); let client = new NeatoApi.Client();
try try
@ -76,16 +67,12 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
return; return;
} }
debug("Found " + robots.length + " robots"); this.log.debug("Neato account has " + robots.length + " robot " + (robots.length == 1 ? "" : "s"));
let loadedRobots = 0;
for (let robot of robots) for (let robot of robots)
{ {
// Get additional information for the robot // Get additional information for the robot
robot.getState((error, state) => { robot.getState((error, state) => {
this.log.debug("Got state for robot: " + robot.name);
robot.meta = state.meta;
if (error) if (error)
{ {
this.log.error("Error getting robot meta information: " + error + ": " + state); this.log.error("Error getting robot meta information: " + error + ": " + state);
@ -94,32 +81,25 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
try try
{ {
robot.meta = state.meta;
const uuid = this.api.hap.uuid.generate(robot._serial); const uuid = this.api.hap.uuid.generate(robot._serial);
const existingAccessory = this.robotAccessories.find(accessory => accessory.UUID === uuid); const existingAccessory = this.robotAccessories.find(accessory => accessory.UUID === uuid);
// the accessory already exists
if (existingAccessory) if (existingAccessory)
{ {
// the accessory already exists this.log.info("[" + robot.name + "] Robot loaded from cache");
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
existingAccessory.context.robot = robot;
// TODO update maps // TODO update maps
// if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: existingAccessory.context.robot = robot;
// existingAccessory.context.device = device; this.api.updatePlatformAccessories([existingAccessory]);
// this.api.updatePlatformAccessories([existingAccessory]);
// create the accessory handler for the restored accessory
// this is imported from `platformAccessory.ts`
new NeatoVacuumRobotAccessory(this, existingAccessory, false, this.config); new NeatoVacuumRobotAccessory(this, existingAccessory, false, this.config);
// it is possible to remove platform accessories at any time using `api.unregisterPlatformAccessories`, eg.:
// remove platform accessories when no longer present
// this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]);
// this.log.info('Removing existing accessory from cache:', existingAccessory.displayName);
} }
else else
{ {
this.log.info('Adding new accessory: ', robot.name); this.log.info("[" + robot.name + "] Robot created");
const accessory = new this.api.platformAccessory(robot.name, uuid); const accessory = new this.api.platformAccessory(robot.name, uuid);
accessory.context.robot = robot; accessory.context.robot = robot;

18
src/models/options.ts Normal file
View File

@ -0,0 +1,18 @@
import {HomebridgeNeatoPlatform} from "../homebridgeNeatoPlatform";
import {PlatformAccessory, PlatformConfig} from "homebridge";
export class Options
{
public eco: boolean;
public extraCare: boolean;
public noGoLines: boolean;
public spot: any;
constructor()
{
this.eco = false;
this.extraCare = false;
this.noGoLines = false;
this.spot = {};
}
}