14 Commits

Author SHA1 Message Date
Luis Riegger
c847481487 Include Include Robot name in Homekit battery service name 2021-02-07 15:27:57 +01:00
Luis Riegger
cc99ecef0d add german plugin language 2021-02-07 11:15:08 +01:00
Luis Riegger
721db866ff remove _config.yml 2021-02-07 10:25:02 +01:00
Luis R
6821adc1e3 Update README.md 2020-12-13 22:51:05 +01:00
Luis R
df52bd103f Update README.md 2020-12-13 22:49:48 +01:00
Luis Riegger
28cdabe47c correct typos in config.schema.json 2020-12-13 22:20:01 +01:00
Luis Riegger
6fd2e46bff Update README 2020-12-13 18:33:13 +01:00
Luis Riegger
a99359f6ac adapt for node-kobold-control api 2020-12-13 18:31:44 +01:00
Luis Riegger
49e3fdf191 adapt package dependency 2020-12-13 10:23:59 +01:00
Luis Riegger
295c9b01a1 Further adapt README.md, Licence and Package 2020-12-12 15:52:26 +01:00
Luis Riegger
eecf7f21f8 Adapt README.md 2020-12-12 15:42:06 +01:00
Arne
5ff3951668 Update README.md 2020-10-08 12:10:43 +02:00
Arne
b83d30cfad Update README.md 2020-03-14 00:33:45 +01:00
Arne
b53ef4ff2f Update README.md 2020-03-14 00:32:48 +01:00
8 changed files with 177 additions and 112 deletions

View File

@@ -116,4 +116,11 @@
* Fixed robot not shown before setting up a floor plan * Fixed robot not shown before setting up a floor plan
## 0.7.2 ## 0.7.2
* Fixed homebridge crash with multiple robots per account * Fixed homebridge crash with multiple robots per account
## 0.8.0
* Add German plugin language (for example, this gives you a "Sauge Küche" Siri command for a zone called "Küche")
* Added possibility to toggle between languages (English/German) in Homebridge UI Plugin Settings
## 0.8.1
* Include Robot name in Homekit battery service name

View File

@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2017 Arne Blumentritt Copyright (c) 2020 Luis Riegger
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

105
README.md
View File

@@ -1,15 +1,10 @@
# homebridge-neato # homebridge-kobold
[![npm](https://img.shields.io/npm/v/homebridge-neato)](https://www.npmjs.com/package/homebridge-neato)
[![npm](https://img.shields.io/npm/dt/homebridge-neato)](https://www.npmjs.com/package/homebridge-neato?activeTab=versions)
[![GitHub last commit](https://img.shields.io/github/last-commit/naofireblade/homebridge-neato)](https://github.com/naofireblade/homebridge-neato)
This is a plugin for [homebridge](https://github.com/nfarina/homebridge) to control your [Neato](https://www.neatorobotics.com/) vacuum robot. You can download it via [npm](https://www.npmjs.com/package/homebridge-neato). This is a plugin for [homebridge](https://github.com/nfarina/homebridge) to control your [Vorwerk Kobold](https://kobold.vorwerk.de/saugroboter/) VR300 vacuum robot. You can download it via [npm](https://www.npmjs.com/package/homebridge-kobold).
If you like this plugin and find it useful, I would be forever grateful for your support: It is based on a fork of naofireblade's [homebridge-neato](https://github.com/naofireblade/homebridge-neato), merged with the oAuth authentication mechanism from nicoh88's [homebridge-vorwerk](https://github.com/nicoh88/homebridge-vorwerk).
<a href="https://www.buymeacoffee.com/2D1nUuK36" target="_blank"><img width="140" src="https://bmc-cdn.nyc3.digitaloceanspaces.com/BMC-button-images/custom_images/orange_img.png" alt="Buy Me A Coffee"></a> The interaction with the Server is handled by the underlying [node-kobold-control](https://github.com/himbeles/node-kobold-control) module.
Feel free to leave any feedback [here](https://github.com/naofireblade/homebridge-neato/issues).
## Features ## Features
@@ -17,10 +12,10 @@ Feel free to leave any feedback [here](https://github.com/naofireblade/homebridg
- Eco mode - Eco mode
- Extra care navigation - Extra care navigation
- Nogo lines - Nogo lines
- Zone cleaning <sup>[1](#d7)</sup><sup>, </sup><sup>[2](#change-room)</sup> - Zone cleaning <sup>[1](#change-room)</sup>
- Spot cleaning - Spot cleaning
- Individual spot size <sup>[1](#d7)</sup> - Individual spot size <sup>[2](#eve)</sup>
- Clean twice - Clean twice <sup>[2](#eve)</sup>
- Return to dock - Return to dock
- Find the robot - Find the robot
- Schedule (de)activation - Schedule (de)activation
@@ -32,33 +27,76 @@ Feel free to leave any feedback [here](https://github.com/naofireblade/homebridg
- Automatic or periodic refresh of robot state - Automatic or periodic refresh of robot state
- Multiple robots - Multiple robots
> <b name="d7">1</b> Only available on the Neato D7. - German or English Language Setting
> <b name="change-room">2</b> You can send the robot from one room to another as well. He will return to the base, wait there some seconds and then starts cleaning the next room. > <b name="change-room">2</b> You can send the robot from one room to another as well. He will return to the base, wait there some seconds and then starts cleaning the next room.
> <b name="eve">3</b> You need a third party app like eve to access these features.
## Installation ## Installation
1. Install homebridge using: `npm install -g homebridge` 1. Install homebridge using: `npm install -g homebridge`
2. Install this plugin using: `npm install -g homebridge-neato` 2. Install this plugin using: `npm install -g homebridge-kobold`
3. If you don't have a Neato account yet, create one [here](https://www.neatorobotics.com/create-account/). 3. Update your configuration file. See the sample below.
4. Update your configuration file. See the sample below.
## Configuration ## Configuration
Add the following information to your config file. Change the values for email and password. Add the following information to your config file. Change the values for email and password.
### Simple ### Simple
```json ```json
"platforms": [ "platforms": [
{ {
"platform": "NeatoVacuumRobot", "platform": "KoboldVacuumRobot",
"email": "YourEmail", "token": "YourToken",
"password": "YourPassword" "language": "de"
} }
] ]
``` ```
You can get a token using the following two curl commands:
```bash
# This will trigger the email sending
curl -X "POST" "https://mykobold.eu.auth0.com/passwordless/start" \
-H 'Content-Type: application/json' \
-d '{
"send": "code",
"email": "ENTER_YOUR_EMAIL_HERE",
"client_id": "KY4YbVAvtgB7lp8vIbWQ7zLk3hssZlhR",
"connection": "email"
}'
```
==== wait for the email to be received ====
```bash
# this will generate a token using the numbers you received via email
# replace the value of otp 123456 with the value you received from the email
curl -X "POST" "https://mykobold.eu.auth0.com/oauth/token" \
-H 'Content-Type: application/json' \
-d '{
"prompt": "login",
"grant_type": "http://auth0.com/oauth/grant-type/passwordless/otp",
"scope": "openid email profile read:current_user",
"locale": "en",
"otp": "123456",
"source": "vorwerk_auth0",
"platform": "ios",
"audience": "https://mykobold.eu.auth0.com/userinfo",
"username": "ENTER_YOUR_EMAIL_HERE",
"client_id": "KY4YbVAvtgB7lp8vIbWQ7zLk3hssZlhR",
"realm": "email",
"country_code": "DE"
}'
```
From the output, you want to copy the `id_token` value.
The `language` can be `de` for German, or `en` for English.
### Advanced ### Advanced
Below are explanations for advanced parameters to adjust the plugin to your needs. All parameters are *optional*. Below are explanations for advanced parameters to adjust the plugin to your needs. All parameters are *optional*.
@@ -74,23 +112,16 @@ List of plugin features that you don't want to use in homekit (e.g. `dock`, `doc
```json ```json
"platforms": [ "platforms": [
{ {
"platform": "NeatoVacuumRobot", "platform": "KoboldVacuumRobot",
"email": "YourEmail", "token": "YourToken",
"password": "YourPassword", "refresh": "120",
"refresh": "120", "hidden": ["dock", "dockstate", "eco", "nogolines", "extracare", "schedule", "find", "spot"],
"hidden": ["dock", "dockstate", "eco", "nogolines", "extracare", "schedule", "find", "spot"] "language": "de"
} }
] ]
``` ```
## Tested robots ## Tested robots
The plugin is successfully tested with all Neato Connected Robots. - Vorwerk Kobold VR300
## Contributors
Many thanks go to
- [ghulands](https://github.com/ghulands) for finding and fixing a bug when no robot is associated with the neato account
- [Berkay](https://github.com/btutal) for adding the schema file to use the plugin with homebridge-config-ui-x
- [Antoine de Maleprade](https://github.com/az0uz) for adding the zone cleaning feature
- [DJay](https://github.com/DJay-X) for testing out tons of new beta versions

View File

@@ -1 +0,0 @@
theme: jekyll-theme-cayman

View File

@@ -1,4 +1,4 @@
const debug = require('debug')('homebridge-neato'); const debug = require('debug')('homebridge-kobold');
const colors = require('colors'); const colors = require('colors');
const CustomUUID = { const CustomUUID = {
@@ -21,10 +21,10 @@ module.exports = function (_Service, _Characteristic)
SpotHeightCharacteristic = require('../characteristics/spotHeight')(Characteristic, CustomUUID); SpotHeightCharacteristic = require('../characteristics/spotHeight')(Characteristic, CustomUUID);
SpotRepeatCharacteristic = require('../characteristics/spotRepeat')(Characteristic, CustomUUID); SpotRepeatCharacteristic = require('../characteristics/spotRepeat')(Characteristic, CustomUUID);
return NeatoVacuumRobotAccessory; return KoboldVacuumRobotAccessory;
}; };
function NeatoVacuumRobotAccessory(platform, robotObject) function KoboldVacuumRobotAccessory(platform, robotObject)
{ {
this.platform = platform; this.platform = platform;
this.log = platform.log; this.log = platform.log;
@@ -38,6 +38,35 @@ function NeatoVacuumRobotAccessory(platform, robotObject)
this.spotPlusFeatures = ((typeof robotObject.availableServices.spotCleaning !== 'undefined') && robotObject.availableServices.spotCleaning.includes("basic")); this.spotPlusFeatures = ((typeof robotObject.availableServices.spotCleaning !== 'undefined') && robotObject.availableServices.spotCleaning.includes("basic"));
this.boundary = (typeof robotObject.boundary === 'undefined') ? null : robotObject.boundary; this.boundary = (typeof robotObject.boundary === 'undefined') ? null : robotObject.boundary;
this.dict = {
'en': {
"clean": "Clean",
"clean the": "Clean the",
"goToDock": "Go to Dock",
"dockState": "Dock",
"eco": "Eco Mode",
"noGoLines": "NoGo Lines",
"extraCare": "Extra Care",
"schedule": "Schedule",
"findMe": "Find me",
"cleanSpot": "Clean Spot",
"battery": "Battery"
},
'de': {
"clean": "Sauge",
"clean the": "Sauge",
"goToDock": "Zur Basis",
"dockState": "In der Basis",
"eco": "Eco Modus",
"noGoLines": "NoGo Linien",
"extraCare": "Extra Care",
"schedule": "Zeitplan",
"findMe": "Finde mich",
"cleanSpot": "Spot Reinigung",
"battery": "Batterie"
}
}[this.platform.language]
if (this.boundary == null) if (this.boundary == null)
{ {
this.name = this.robot.name; this.name = this.robot.name;
@@ -64,20 +93,20 @@ function NeatoVacuumRobotAccessory(platform, robotObject)
this.name = this.robot.name + ' - ' + this.boundary.name; this.name = this.robot.name + ' - ' + this.boundary.name;
} }
this.batteryService = new Service.BatteryService("Battery", "battery"); this.batteryService = new Service.BatteryService(this.name + " " + this.dict["battery"], "battery");
if (this.boundary == null) if (this.boundary == null)
{ {
this.cleanService = new Service.Switch(this.name + " Clean", "clean"); this.cleanService = new Service.Switch(this.name + " " + this.dict["clean"], "clean");
this.goToDockService = new Service.Switch(this.name + " Go to Dock", "goToDock"); this.goToDockService = new Service.Switch(this.name + " " + this.dict["goToDock"], "goToDock");
this.dockStateService = new Service.OccupancySensor(this.name + " Dock", "dockState"); this.dockStateService = new Service.OccupancySensor(this.name + " " + this.dict["dockState"], "dockState");
this.ecoService = new Service.Switch(this.name + " Eco Mode", "eco"); this.ecoService = new Service.Switch(this.name + " " + this.dict["eco"], "eco");
this.noGoLinesService = new Service.Switch(this.name + " NoGo Lines", "noGoLines"); this.noGoLinesService = new Service.Switch(this.name + " " + this.dict["noGoLines"], "noGoLines");
this.extraCareService = new Service.Switch(this.name + " Extra Care", "extraCare"); this.extraCareService = new Service.Switch(this.name + " " + this.dict["extraCare"], "extraCare");
this.scheduleService = new Service.Switch(this.name + " Schedule", "schedule"); this.scheduleService = new Service.Switch(this.name + " " + this.dict["schedule"], "schedule");
this.findMeService = new Service.Switch(this.name + " Find Me", "findMe"); this.findMeService = new Service.Switch(this.name + " " + this.dict["findMe"], "findMe");
this.spotCleanService = new Service.Switch(this.name + " Clean Spot", "cleanSpot"); this.spotCleanService = new Service.Switch(this.name + " " + this.dict["cleanSpot"], "cleanSpot");
this.spotCleanService.addCharacteristic(SpotRepeatCharacteristic); this.spotCleanService.addCharacteristic(SpotRepeatCharacteristic);
if (this.spotPlusFeatures) if (this.spotPlusFeatures)
{ {
@@ -88,10 +117,10 @@ function NeatoVacuumRobotAccessory(platform, robotObject)
else else
{ {
const splitName = this.boundary.name.split(' '); const splitName = this.boundary.name.split(' ');
let serviceName = "Clean the " + this.boundary.name; let serviceName = this.dict["clean the"] + " " + this.boundary.name;
if (splitName.length >= 2 && splitName[splitName.length - 2].match(/[']s$/g)) if (splitName.length >= 2 && splitName[splitName.length - 2].match(/[']s$/g))
{ {
serviceName = "Clean " + this.boundary.name; serviceName = this.dict["clean"] + " " + this.boundary.name;
} }
this.cleanService = new Service.Switch(serviceName, "cleanBoundary:" + this.boundary.id); this.cleanService = new Service.Switch(serviceName, "cleanBoundary:" + this.boundary.id);
} }
@@ -99,7 +128,7 @@ function NeatoVacuumRobotAccessory(platform, robotObject)
this.log("Added cleaning device named: " + this.name); this.log("Added cleaning device named: " + this.name);
} }
NeatoVacuumRobotAccessory.prototype = { KoboldVacuumRobotAccessory.prototype = {
identify: function (callback) identify: function (callback)
{ {
this.robot.getState((error, result) => this.robot.getState((error, result) =>
@@ -121,7 +150,7 @@ NeatoVacuumRobotAccessory.prototype = {
{ {
this.informationService = new Service.AccessoryInformation(); this.informationService = new Service.AccessoryInformation();
this.informationService this.informationService
.setCharacteristic(Characteristic.Manufacturer, "Neato Robotics") .setCharacteristic(Characteristic.Manufacturer, "Vorwerk Deutschland Stiftung & Co. KG")
.setCharacteristic(Characteristic.Model, this.meta.modelName) .setCharacteristic(Characteristic.Model, this.meta.modelName)
.setCharacteristic(Characteristic.SerialNumber, this.robot._serial) .setCharacteristic(Characteristic.SerialNumber, this.robot._serial)
.setCharacteristic(Characteristic.FirmwareRevision, this.meta.firmware) .setCharacteristic(Characteristic.FirmwareRevision, this.meta.firmware)

View File

@@ -1,22 +1,35 @@
{ {
"pluginAlias": "NeatoVacuumRobot", "pluginAlias": "KoboldVacuumRobot",
"pluginType": "platform", "pluginType": "platform",
"headerDisplay": "For Advanced settings like Refresh time interval or Disabled switches/sensors. [Check Here](https://github.com/naofireblade/homebridge-neato#readme)", "headerDisplay": "For Advanced settings like the refresh time interval or disabled switches/sensors. [Check Here](https://github.com/himbeles/homebridge-kobold#readme)",
"schema": { "schema": {
"type": "object", "type": "object",
"properties": { "properties": {
"email": { "token": {
"title": "email", "title": "token",
"type": "string",
"required": true,
"format": "email",
"description": "Your Email Address"
},
"password": {
"title": "password",
"type": "string", "type": "string",
"required": true, "required": true,
"description": "Your Password" "description": "Your Token"
},
"language": {
"title": "language",
"type": "string",
"default": "en",
"oneOf": [
{
"title": "English",
"enum": [
"en"
]
},
{
"title": "German",
"enum": [
"de"
]
}
],
"required": true
} }
} }
} }

View File

@@ -1,26 +1,26 @@
"use strict"; "use strict";
let inherits = require('util').inherits, let inherits = require('util').inherits,
debug = require('debug')('homebridge-neato'), debug = require('debug')('homebridge-kobold'),
botvac = require('node-botvac'), control = require('node-kobold-control'),
Service, Service,
Characteristic, Characteristic,
NeatoVacuumRobotAccessory; KoboldVacuumRobotAccessory;
module.exports = function (homebridge) module.exports = function (homebridge)
{ {
Service = homebridge.hap.Service; Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic; Characteristic = homebridge.hap.Characteristic;
NeatoVacuumRobotAccessory = require('./accessories/neatoVacuumRobot')(Service, Characteristic); KoboldVacuumRobotAccessory = require('./accessories/koboldVacuumRobot')(Service, Characteristic);
homebridge.registerPlatform("homebridge-neato", "NeatoVacuumRobot", NeatoVacuumRobotPlatform); homebridge.registerPlatform("homebridge-kobold", "KoboldVacuumRobot", KoboldVacuumRobotPlatform);
}; };
function NeatoVacuumRobotPlatform(log, config) function KoboldVacuumRobotPlatform(log, config)
{ {
this.log = log; this.log = log;
this.serial = "1-3-3-7"; this.serial = "1-3-3-7";
this.email = config['email']; this.token = config['token'];
this.password = config['password']; this.language = config['language'];
this.hiddenServices = ''; this.hiddenServices = '';
this.hiddenServices = ('disabled' in config ? config['disabled'] : this.hiddenServices); this.hiddenServices = ('disabled' in config ? config['disabled'] : this.hiddenServices);
this.hiddenServices = ('hidden' in config ? config['hidden'] : this.hiddenServices); this.hiddenServices = ('hidden' in config ? config['hidden'] : this.hiddenServices);
@@ -35,10 +35,10 @@ function NeatoVacuumRobotPlatform(log, config)
this.refresh = parseInt(config['refresh']); this.refresh = parseInt(config['refresh']);
// must be integer and positive // must be integer and positive
this.refresh = (typeof this.refresh !== 'number' || (this.refresh % 1) !== 0 || this.refresh < 0) ? 60 : this.refresh; this.refresh = (typeof this.refresh !== 'number' || (this.refresh % 1) !== 0 || this.refresh < 0) ? 60 : this.refresh;
// minimum 60s to save some load on the neato servers // minimum 60s to save some load on the Vorwerk servers
if (this.refresh > 0 && this.refresh < 60) if (this.refresh > 0 && this.refresh < 60)
{ {
this.log.warn("Minimum refresh time is 60 seconds to not overload the neato servers"); this.log.warn("Minimum refresh time is 60 seconds to not overload the Vorwerk servers");
this.refresh = (this.refresh > 0 && this.refresh < 60) ? 60 : this.refresh; this.refresh = (this.refresh > 0 && this.refresh < 60) ? 60 : this.refresh;
} }
} }
@@ -50,7 +50,7 @@ function NeatoVacuumRobotPlatform(log, config)
this.log("Refresh is set to: " + this.refresh + (this.refresh !== 'auto' ? ' seconds' : '')); this.log("Refresh is set to: " + this.refresh + (this.refresh !== 'auto' ? ' seconds' : ''));
} }
NeatoVacuumRobotPlatform.prototype = { KoboldVacuumRobotPlatform.prototype = {
accessories: function (callback) accessories: function (callback)
{ {
debug("Get robots"); debug("Get robots");
@@ -60,8 +60,8 @@ NeatoVacuumRobotPlatform.prototype = {
this.getRobots(() => this.getRobots(() =>
{ {
// // MOCK MULTIPLE ROBOTS START // // MOCK MULTIPLE ROBOTS START
// let client = new botvac.Client(); // let client = new control.Client();
// client.authorize(this.email, this.password, false, (error) => // client.authorize(this.token, (error) =>
// { // {
// client.getRobots((error, robs) => // client.getRobots((error, robs) =>
// { // {
@@ -76,7 +76,7 @@ NeatoVacuumRobotPlatform.prototype = {
{ {
this.log("Found robot #" + (i + 1) + " named \"" + robot.device.name + "\" with serial \"" + robot.device._serial.substring(0, 9) + "XXXXXXXXXXXX\""); this.log("Found robot #" + (i + 1) + " named \"" + robot.device.name + "\" with serial \"" + robot.device._serial.substring(0, 9) + "XXXXXXXXXXXX\"");
let mainAccessory = new NeatoVacuumRobotAccessory(this, robot); let mainAccessory = new KoboldVacuumRobotAccessory(this, robot);
accessories.push(mainAccessory); accessories.push(mainAccessory);
robot.mainAccessory = mainAccessory; robot.mainAccessory = mainAccessory;
@@ -87,7 +87,7 @@ NeatoVacuumRobotPlatform.prototype = {
// // MOCK ZONE CLEANING START // // MOCK ZONE CLEANING START
// robot.boundary = {name: "Testroom", id: "1"}; // robot.boundary = {name: "Testroom", id: "1"};
// let roomAccessory = new NeatoVacuumRobotAccessory(this, robot); // let roomAccessory = new KoboldVacuumRobotAccessory(this, robot);
// accessories.push(roomAccessory); // accessories.push(roomAccessory);
// robot.roomAccessories.push(roomAccessory); // robot.roomAccessories.push(roomAccessory);
// // MOCK ZONE CLEANING END // // MOCK ZONE CLEANING END
@@ -103,7 +103,7 @@ NeatoVacuumRobotPlatform.prototype = {
if (boundary.type === "polygon") if (boundary.type === "polygon")
{ {
robot.boundary = boundary; robot.boundary = boundary;
let roomAccessory = new NeatoVacuumRobotAccessory(this, robot); let roomAccessory = new KoboldVacuumRobotAccessory(this, robot);
accessories.push(roomAccessory); accessories.push(roomAccessory);
robot.roomAccessories.push(roomAccessory); robot.roomAccessories.push(roomAccessory);
@@ -126,14 +126,14 @@ NeatoVacuumRobotPlatform.prototype = {
getRobots: function (callback) getRobots: function (callback)
{ {
debug("Loading your robots"); debug("Loading your robots");
let client = new botvac.Client(); let client = new control.Client();
// Login // Login
client.authorize(this.email, this.password, false, (error) => client.authorize(this.token, (error) =>
{ {
if (error) if (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 Vorwerk cloud. Please check your internet connection and your token. Try again later if the Vorwerk servers have issues: " + error);
callback(); callback();
} }
else else
@@ -143,7 +143,7 @@ NeatoVacuumRobotPlatform.prototype = {
{ {
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 connect to your Vorwerk robot: " + error);
callback(); callback();
} }
else if (robots.length === 0) else if (robots.length === 0)

View File

@@ -1,43 +1,29 @@
{ {
"name": "homebridge-neato", "name": "homebridge-kobold",
"version": "0.7.2", "version": "0.8.0",
"description": "A Neato vacuum robot plugin for homebridge.", "description": "A Vorwerk Kobold vacuum robot plugin for homebridge.",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
"homebridge-plugin", "homebridge-plugin",
"neato", "vorwerk",
"botvac" "kobold"
], ],
"engines": { "engines": {
"node": ">=0.12.0", "node": ">=0.12.0",
"homebridge": ">=0.2.0" "homebridge": ">=0.2.0"
}, },
"author": { "author": {
"name": "Arne Blumentritt", "name": "Luis R.",
"url2": "https://github.com/naofireblade" "url2": "https://github.com/himbeles"
}, },
"contributors": [
{
"name": "ghulands",
"url": "https://github.com/ghulands"
},
{
"name": "Berkay",
"url": "https://github.com/btutal"
},
{
"name": "Antoine de Maleprade",
"url": "https://github.com/az0uz"
}
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/naofireblade/homebridge-neato.git" "url": "git://github.com/himbeles/homebridge-kobold.git"
}, },
"dependencies": { "dependencies": {
"colors": "^1.4.0", "colors": "^1.4.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"node-botvac": ">=0.4.0", "node-kobold-control": ">=0.4.0",
"uuid": "^3.3.2" "uuid": "^3.3.2"
} }
} }