WIP dynamic platform
This commit is contained in:
parent
e0bd97ee5d
commit
b13885bea7
14
package.json
14
package.json
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
@ -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
18
src/models/options.ts
Normal 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 = {};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user