Added spot cleaning

This commit is contained in:
Arne Blumentritt 2021-05-09 20:34:23 +02:00
parent 7a0758f883
commit d9b5c7f572
4 changed files with 116 additions and 91 deletions

View File

@ -132,8 +132,7 @@
* Changed config parameter **hidden**. Renamed to **services**, now takes list of services that should be _visible_. Default are all available services. * Changed config parameter **hidden**. Renamed to **services**, now takes list of services that should be _visible_. Default are all available services.
* Fixed robots no longer disappear or change the room after connection issues with the Neato API * Fixed robots no longer disappear or change the room after connection issues with the Neato API
* Fixed plugin no longer crashes if non smart robot is assigned in neato account * Fixed plugin no longer crashes if non smart robot is assigned in neato account
* Fixed options for eco, nogo lines, extra care are now saved in homebridge and will no longer be overridden by Neato API * Fixed options for eco, nogo lines, extra care, spot repeat, spot size are now saved in homebridge and will no longer be overridden by Neato API
## TODO until 1.0.0 release ## TODO until 1.0.0 release
* Room cleaning * Room cleaning
* Spot size

View File

@ -1,6 +1,10 @@
import {CharacteristicValue, Logger, PlatformAccessory, PlatformAccessoryEvent, PlatformConfig, Service} from 'homebridge'; import {CharacteristicValue, Logger, PlatformAccessory, PlatformAccessoryEvent, PlatformConfig, Service} from 'homebridge';
import {HomebridgeNeatoPlatform} from '../homebridgeNeatoPlatform'; import {HomebridgeNeatoPlatform} from '../homebridgeNeatoPlatform';
import spotRepeat from '../characteristics/spotRepeat';
import spotWidth from '../characteristics/spotWidth';
import spotHeight from '../characteristics/spotHeight';
import {Options} from '../models/options'; import {Options} from '../models/options';
import {CharacteristicGetHandler, CharacteristicSetHandler} from "hap-nodejs/dist/lib/Characteristic";
/** /**
* Platform Accessory * Platform Accessory
@ -22,6 +26,7 @@ export class NeatoVacuumRobotAccessory
private readonly extraCareService: Service | null; private readonly extraCareService: Service | null;
private readonly scheduleService: Service | null; private readonly scheduleService: Service | null;
private readonly spotCleanService: Service | null; private readonly spotCleanService: Service | null;
private spotPlusFeatures: boolean;
// Context // Context
private robot: any; private robot: any;
@ -50,6 +55,7 @@ export class NeatoVacuumRobotAccessory
this.robot = accessory.context.robot; this.robot = accessory.context.robot;
this.options = accessory.context.options || new Options(); this.options = accessory.context.options || new Options();
this.spotPlusFeatures = false;
this.backgroundUpdateInterval = NeatoVacuumRobotAccessory.parseBackgroundUpdateInterval(this.config['backgroundUpdate']); this.backgroundUpdateInterval = NeatoVacuumRobotAccessory.parseBackgroundUpdateInterval(this.config['backgroundUpdate']);
this.prefix = this.config['prefix'] || PREFIX; this.prefix = this.config['prefix'] || PREFIX;
@ -82,75 +88,22 @@ export class NeatoVacuumRobotAccessory
}); });
// Services // Services
this.cleanService = this.getSwitchService(RobotService.CLEAN_HOUSE); this.cleanService = this.getSwitchService(RobotService.CLEAN_HOUSE, this.getCleanHouse.bind(this), this.setCleanHouse.bind(this));
this.spotCleanService = this.getSwitchService(RobotService.CLEAN_SPOT); this.spotCleanService = this.getSwitchService(RobotService.CLEAN_SPOT, this.getSpotClean.bind(this), this.setSpotClean.bind(this));
this.goToDockService = this.getSwitchService(RobotService.GO_TO_DOCK); this.goToDockService = this.getSwitchService(RobotService.GO_TO_DOCK, this.getGoToDock.bind(this), this.setGoToDock.bind(this));
this.dockStateService = this.getOccupancyService(RobotService.DOCKED) this.dockStateService = this.getOccupancyService(RobotService.DOCKED, this.getDocked.bind(this))
this.binFullService = this.getOccupancyService(RobotService.BIN_FULL) this.binFullService = this.getOccupancyService(RobotService.BIN_FULL, this.getBinFull.bind(this))
this.findMeService = this.getSwitchService(RobotService.FIND_ME); this.findMeService = this.getSwitchService(RobotService.FIND_ME, this.getFindMe.bind(this), this.setFindMe.bind(this));
this.scheduleService = this.getSwitchService(RobotService.SCHEDULE); this.scheduleService = this.getSwitchService(RobotService.SCHEDULE, this.getSchedule.bind(this), this.setSchedule.bind(this));
this.ecoService = this.getSwitchService(RobotService.ECO); this.ecoService = this.getSwitchService(RobotService.ECO, this.getEco.bind(this), this.setEco.bind(this));
this.noGoLinesService = this.getSwitchService(RobotService.NOGO_LINES); this.noGoLinesService = this.getSwitchService(RobotService.NOGO_LINES, this.getNoGoLines.bind(this), this.setNoGoLines.bind(this));
this.extraCareService = this.getSwitchService(RobotService.EXTRA_CARE); this.extraCareService = this.getSwitchService(RobotService.EXTRA_CARE, this.getExtraCare.bind(this), this.setExtraCare.bind(this));
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 should be the main switch if the accessory is grouped in homekit
if (this.cleanService) if (this.cleanService)
{ {
this.cleanService.getCharacteristic(this.platform.Characteristic.On) this.cleanService.setPrimaryService(true);
.onSet(this.setCleanHouse.bind(this))
.onGet(this.getCleanHouse.bind(this));
}
if (this.spotCleanService)
{
this.spotCleanService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setSpotClean.bind(this))
.onGet(this.getSpotClean.bind(this));
}
if (this.goToDockService)
{
this.goToDockService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setGoToDock.bind(this))
.onGet(this.getGoToDock.bind(this));
}
if (this.dockStateService)
{
this.dockStateService.getCharacteristic(this.platform.Characteristic.OccupancyDetected)
.onGet(this.getDocked.bind(this));
}
if (this.binFullService)
{
this.binFullService.getCharacteristic(this.platform.Characteristic.OccupancyDetected)
.onGet(this.getBinFull.bind(this));
}
if (this.findMeService)
{
this.findMeService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setFindMe.bind(this))
.onGet(this.getFindMe.bind(this));
}
if (this.scheduleService)
{
this.scheduleService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setSchedule.bind(this))
.onGet(this.getSchedule.bind(this));
}
if (this.ecoService)
{
this.ecoService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setEco.bind(this))
.onGet(this.getEco.bind(this));
}
if (this.noGoLinesService)
{
this.noGoLinesService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setNoGoLines.bind(this))
.onGet(this.getNoGoLines.bind(this));
}
if (this.extraCareService)
{
this.extraCareService.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setExtraCare.bind(this))
.onGet(this.getExtraCare.bind(this));
} }
// Start background update // Start background update
@ -162,6 +115,10 @@ export class NeatoVacuumRobotAccessory
this.options.extraCare = this.robot.navigationMode == 2; this.options.extraCare = this.robot.navigationMode == 2;
this.debug(DebugType.INFO, "Options initially set to eco: " + this.options.eco + ", noGoLines: " + this.options.noGoLines + ", extraCare: " + this.options.extraCare); 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; accessory.context.options = this.options;
// Add special characteristics to set spot cleaning options
this.spotPlusFeatures = ((typeof this.robot.availableServices.spotCleaning !== 'undefined') && this.robot.availableServices.spotCleaning.includes("basic"));
this.addSpotCleanCharacteristics();
} }
else else
{ {
@ -170,13 +127,43 @@ export class NeatoVacuumRobotAccessory
}); });
} }
private getSwitchService(serviceName: string) private addSpotCleanCharacteristics()
{
// Only add characteristics of service is available ond characteristics are not added yet
if (this.spotCleanService != null && !this.options.spotCharacteristics)
{
this.spotCleanService.addCharacteristic(spotRepeat(this.platform.Characteristic))
.onGet(this.getSpotRepeat.bind(this))
.onSet(this.setSpotRepeat.bind(this));
// Add these only if the robot supports them
if (this.spotPlusFeatures)
{
this.spotCleanService.addCharacteristic(spotWidth(this.platform.Characteristic))
.onGet(this.getSpotWidth.bind(this))
.onSet(this.setSpotWidth.bind(this));
this.spotCleanService.addCharacteristic(spotHeight(this.platform.Characteristic))
.onGet(this.getSpotHeight.bind(this))
.onSet(this.setSpotHeight.bind(this));
}
this.options.spotCharacteristics = true;
}
else if (this.spotCleanService == null)
{
this.options.spotCharacteristics = false;
}
}
private getSwitchService(serviceName: string, getHandler: CharacteristicGetHandler, setHandler: CharacteristicSetHandler)
{ {
let displayName = this.prefix ? this.robot.name + serviceName : serviceName; let displayName = this.prefix ? this.robot.name + serviceName : serviceName;
if (this.availableServices.includes(serviceName)) if (this.availableServices.includes(serviceName))
{ {
return this.accessory.getService(displayName) || this.accessory.addService(this.platform.Service.Switch, displayName, serviceName); let service = this.accessory.getService(displayName) || this.accessory.addService(this.platform.Service.Switch, displayName, serviceName);
service.getCharacteristic(this.platform.Characteristic.On)
.onGet(getHandler)
.onSet(setHandler);
return service;
} }
else else
{ {
@ -184,17 +171,20 @@ export class NeatoVacuumRobotAccessory
{ {
this.accessory.removeService(<Service>this.accessory.getService(displayName)); this.accessory.removeService(<Service>this.accessory.getService(displayName));
} }
return null; return null
} }
} }
private getOccupancyService(serviceName: string) private getOccupancyService(serviceName: string, getHandler: CharacteristicGetHandler)
{ {
let displayName = this.prefix ? this.robot.name + serviceName : serviceName; let displayName = this.prefix ? this.robot.name + serviceName : serviceName;
if (this.availableServices.includes(serviceName)) if (this.availableServices.includes(serviceName))
{ {
return this.accessory.getService(displayName) || this.accessory.addService(this.platform.Service.OccupancySensor, displayName, serviceName); let service = this.accessory.getService(displayName) || this.accessory.addService(this.platform.Service.OccupancySensor, displayName, serviceName);
service.getCharacteristic(this.platform.Characteristic.OccupancyDetected.OccupancyDetected)
.onGet(getHandler);
return service;
} }
else else
{ {
@ -202,7 +192,7 @@ export class NeatoVacuumRobotAccessory
{ {
this.accessory.removeService(<Service>this.accessory.getService(displayName)); this.accessory.removeService(<Service>this.accessory.getService(displayName));
} }
return null; return null
} }
} }
@ -457,6 +447,39 @@ export class NeatoVacuumRobotAccessory
this.options.noGoLines = <boolean>on; this.options.noGoLines = <boolean>on;
} }
getSpotRepeat()
{
return this.options.spotRepeat;
}
setSpotRepeat(on: CharacteristicValue)
{
this.debug(DebugType.STATUS, "Set SPOT REPEAT: " + on);
this.options.spotRepeat = <boolean>on;
}
getSpotWidth()
{
return this.options.spotWidth;
}
setSpotWidth(length: CharacteristicValue)
{
this.debug(DebugType.STATUS, "Set SPOT WIDTH: " + length + " cm");
this.options.spotWidth = <number>length;
}
getSpotHeight()
{
return this.options.spotHeight;
}
setSpotHeight(length: CharacteristicValue)
{
this.debug(DebugType.STATUS, "Set SPOT HEIGHT: " + length + " cm");
this.options.spotHeight = <number>length;
}
getFindMe() getFindMe()
{ {
return false; return false;
@ -492,7 +515,7 @@ export class NeatoVacuumRobotAccessory
// Enable shorter background update while cleaning // Enable shorter background update while cleaning
setTimeout(() => { setTimeout(() => {
this.updateRobotPeriodically(); this.updateRobotPeriodically();
}, 60 * 1000); }, 2 * 60 * 1000);
this.log.info( this.log.info(
"[" + this.robot.name + "] > Start cleaning with options type: " + CleanType[cleanType] + ", eco: " + this.options.eco + ", noGoLines: " + this.options.noGoLines + ", extraCare: " "[" + this.robot.name + "] > Start cleaning with options type: " + CleanType[cleanType] + ", eco: " + this.options.eco + ", noGoLines: " + this.options.noGoLines + ", extraCare: "
@ -506,7 +529,7 @@ export class NeatoVacuumRobotAccessory
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);
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.spotWidth, this.options.spotHeight, this.options.spotRepeat, this.options.extraCare ? 2 : 1);
break; break;
} }
} }

View File

@ -40,7 +40,6 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
discoverRobots() discoverRobots()
{ {
const client = new NeatoApi.Client(); const client = new NeatoApi.Client();
this.log.debug("blub");
try try
{ {
@ -126,6 +125,7 @@ export class HomebridgeNeatoPlatform implements DynamicPlatformPlugin
try try
{ {
robot.meta = state.meta; robot.meta = state.meta;
robot.availableServices = state.availableServices;
// Update existing robot accessor // Update existing robot accessor
if (cachedRobot) if (cachedRobot)

View File

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