├── homebridge-legacy-plugins ├── .gitmodules ├── api.js ├── .gitignore ├── package.json ├── index.js ├── accessories │ ├── GenericRS232Device.js │ ├── HttpHygrometer.js │ ├── HttpThermometer.js │ ├── mpdclient.js │ ├── ELKM1.js │ ├── HttpGarageDoorOpener.js │ ├── Carwings.js │ ├── HomeMaticWindow.js │ ├── TeslaHorn.js │ ├── TeslaFlash.js │ ├── Tesla.js │ ├── HomeMatic.js │ ├── TeslaSummon.js │ ├── TeslaSummonForward.js │ ├── X10.js │ ├── TeslaUnlock.js │ ├── TeslaSunroof.js │ ├── TeslaChargePort.js │ ├── TeslaGarage.js │ ├── AD2USB.js │ └── HomeMaticThermo.js ├── platforms │ ├── SmartThings.js │ ├── TelldusLive.js │ ├── LIFx.js │ └── ISY.js ├── LICENSE └── config-sample.json ├── lib ├── version.js ├── user.js ├── cli.js ├── logger.js ├── bridgeSetupManager.js ├── api.js ├── plugin.js └── platformAccessory.js ├── bin └── homebridge ├── example-plugins └── homebridge-samplePlatform │ ├── package.json │ └── index.js ├── config-sample.json ├── package.json ├── README.md ├── config.json └── LICENSE /homebridge-legacy-plugins/.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/api.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | homebridge: null // filled in by index.js, accessed later by accessories+platforms 4 | } -------------------------------------------------------------------------------- /homebridge-legacy-plugins/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Mac 3 | .DS_Store 4 | 5 | # Node 6 | node_modules/ 7 | npm-debug.log 8 | .node-version 9 | 10 | # Intellij 11 | .idea/ 12 | *.iml 13 | 14 | # HomeBridge 15 | config.json 16 | config.test.json 17 | persist/ 18 | 19 | 20 | .AppleDouble 21 | -------------------------------------------------------------------------------- /lib/version.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | 4 | 'use strict'; 5 | 6 | module.exports = getVersion(); 7 | 8 | function getVersion() { 9 | var packageJSONPath = path.join(__dirname, '../package.json'); 10 | var packageJSON = JSON.parse(fs.readFileSync(packageJSONPath)); 11 | return packageJSON.version; 12 | } 13 | -------------------------------------------------------------------------------- /bin/homebridge: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // 4 | // This executable sets up the environment and runs the HomeBridge CLI. 5 | // 6 | 7 | 'use strict'; 8 | 9 | process.title = 'homebridge'; 10 | 11 | // Find the HomeBridge lib 12 | var path = require('path'); 13 | var fs = require('fs'); 14 | var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); 15 | 16 | // Run HomeBridge 17 | require(lib + '/cli')(); -------------------------------------------------------------------------------- /example-plugins/homebridge-samplePlatform/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge-samplePlatform", 3 | "version": "0.0.1", 4 | "description": "Sample Platform plugin for homebridge: https://github.com/nfarina/homebridge", 5 | "license": "ISC", 6 | "keywords": [ 7 | "homebridge-plugin" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/example/homebridge.git" 12 | }, 13 | "bugs": { 14 | "url": "http://github.com/example/homebridge/issues" 15 | }, 16 | "engines": { 17 | "node": ">=0.12.0", 18 | "homebridge": ">=0.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /config-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge", 4 | "username": "CC:22:3D:E3:CE:30", 5 | "port": 51826, 6 | "pin": "031-45-154" 7 | }, 8 | 9 | "description": "This is an example configuration file with one fake accessory and one fake platform. You can use this as a template for creating your own configuration file containing devices you actually own.", 10 | 11 | "accessories": [ 12 | { 13 | "accessory": "WeMo", 14 | "name": "Coffee Maker" 15 | } 16 | ], 17 | 18 | "platforms": [ 19 | { 20 | "platform" : "PhilipsHue", 21 | "name" : "Hue" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge", 3 | "description": "HomeKit support for the impatient", 4 | "version": "0.3.4", 5 | "scripts": { 6 | "dev": "DEBUG=* ./bin/homebridge -D -P example-plugins/ || true" 7 | }, 8 | "author": { 9 | "name": "Nick Farina" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/nfarina/homebridge.git" 14 | }, 15 | "bugs": { 16 | "url": "http://github.com/nfarina/homebridge/issues" 17 | }, 18 | "license": "ISC", 19 | "bin": { 20 | "homebridge": "bin/homebridge" 21 | }, 22 | "engines": { 23 | "node": ">=0.12.0" 24 | }, 25 | "preferGlobal": true, 26 | "dependencies": { 27 | "chalk": "^1.1.1", 28 | "commander": "2.8.1", 29 | "hap-nodejs": "0.3.2", 30 | "semver": "5.0.3", 31 | "node-persist": "^0.0.8" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge-legacy-plugins", 3 | "description": "Legacy plugins for homebridge: https://github.com/nfarina/homebridge", 4 | "version": "0.2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/nfarina/homebridge-legacy-plugins.git" 8 | }, 9 | "license": "ISC", 10 | "preferGlobal": true, 11 | "keywords": [ 12 | "homebridge-plugin" 13 | ], 14 | "engines": { 15 | "node": ">=0.12.0", 16 | "homebridge": ">=0.2.5" 17 | }, 18 | "dependencies": { 19 | "ad2usb": "git+https://github.com/alistairg/node-ad2usb.git#local", 20 | "carwingsjs": "0.0.x", 21 | "debug": "^2.2.0", 22 | "eibd": "^0.3.1", 23 | "elkington": "kevinohara80/elkington", 24 | "komponist": "0.1.0", 25 | "lifx": "git+https://github.com/magicmonkey/lifxjs.git", 26 | "lifx-api": "^1.0.1", 27 | "request": "2.49.x", 28 | "telldus-live": "^0.2.1", 29 | "teslams": "1.0.1", 30 | "xml2js": "0.4.x" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/user.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | 4 | 'use strict'; 5 | 6 | module.exports = { 7 | User: User 8 | } 9 | 10 | /** 11 | * Manages user settings and storage locations. 12 | */ 13 | 14 | // global cached config 15 | var config; 16 | 17 | // optional custom storage path 18 | var customStoragePath; 19 | 20 | function User() { 21 | } 22 | 23 | User.config = function() { 24 | return config || (config = Config.load(User.configPath())); 25 | } 26 | 27 | User.storagePath = function() { 28 | return __dirname + '/../'; 29 | // if (customStoragePath) return customStoragePath; 30 | // var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; 31 | // return path.join(home, ".homebridge"); 32 | } 33 | 34 | User.configPath = function() { 35 | console.log(path.join(User.storagePath(), "config.json")); 36 | return path.join(User.storagePath(), "config.json"); 37 | } 38 | 39 | User.persistPath = function() { 40 | return path.join(User.storagePath(), "persist"); 41 | } 42 | 43 | User.cachedAccessoryPath = function() { 44 | return path.join(User.storagePath(), "accessories"); 45 | } 46 | 47 | User.setStoragePath = function(path) { 48 | customStoragePath = path; 49 | } 50 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var api = require('./api') 3 | var fs = require('fs'); 4 | 5 | module.exports = function(homebridge) { 6 | 7 | // make the homebridge API object available to accessories and platforms that weren't 8 | // designed with it in mind 9 | api.homebridge = homebridge; 10 | 11 | // load up all legacy accessories 12 | var accessoriesDir = path.join(__dirname, "accessories"); 13 | 14 | fs.readdirSync(accessoriesDir).forEach(function(file) { 15 | if (file.indexOf(".js") > 0) { 16 | var name = file.replace(".js",""); 17 | homebridge.registerAccessory("homebridge-legacy-plugins", name, function(logger, config) { 18 | console.log("Loading legacy accessory " + name); 19 | 20 | var accessoryModule = require(path.join(accessoriesDir, file)); 21 | return new accessoryModule.accessory(logger, config); 22 | }); 23 | } 24 | }); 25 | 26 | // load up all legacy platforms 27 | var platformsDir = path.join(__dirname, "platforms"); 28 | 29 | fs.readdirSync(platformsDir).forEach(function(file) { 30 | if (file.indexOf(".js") > 0) { 31 | var name = file.replace(".js",""); 32 | homebridge.registerPlatform("homebridge-legacy-plugins", name, function(logger, config) { 33 | console.log("Loading legacy platform " + name); 34 | 35 | var platformModule = require(path.join(platformsDir, file)); 36 | return new platformModule.platform(logger, config); 37 | }); 38 | } 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | var program = require('commander'); 2 | var hap = require("hap-nodejs"); 3 | var version = require('./version'); 4 | var Server = require('./server').Server; 5 | var Plugin = require('./plugin').Plugin; 6 | var User = require('./user').User; 7 | var log = require("./logger")._system; 8 | 9 | 'use strict'; 10 | 11 | module.exports = function() { 12 | 13 | var insecureAccess = false; 14 | 15 | program 16 | .version(version) 17 | .option('-P, --plugin-path [path]', 'look for plugins installed at [path] as well as the default locations ([path] can also point to a single plugin)', function(p) { Plugin.addPluginPath(p); }) 18 | .option('-U, --user-storage-path [path]', 'look for homebridge user files at [path] instead of the default location (~/.homebridge)', function(p) { User.setStoragePath(p); }) 19 | .option('-D, --debug', 'turn on debug level logging', function() { require('./logger').setDebugEnabled(true) }) 20 | .option('-I, --insecure', 'allow unauthenticated requests (for easier hacking)', function() { insecureAccess = true }) 21 | .parse(process.argv); 22 | 23 | // Initialize HAP-NodeJS with a custom persist directory 24 | hap.init(User.persistPath()); 25 | 26 | var server = new Server(insecureAccess); 27 | 28 | var signals = { 'SIGINT': 2, 'SIGTERM': 15 }; 29 | Object.keys(signals).forEach(function (signal) { 30 | process.on(signal, function () { 31 | log.info("Got %s, shutting down Homebridge...", signal); 32 | 33 | // Save cached accessories to persist storage. 34 | server._updateCachedAccessories(); 35 | 36 | process.exit(128 + signals[signal]); 37 | }); 38 | }); 39 | 40 | server.run(); 41 | } 42 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/GenericRS232Device.js: -------------------------------------------------------------------------------- 1 | var Service = require("../api").homebridge.hap.Service; 2 | var Characteristic = require("../api").homebridge.hap.Characteristic; 3 | var SerialPort = require("serialport").SerialPort; 4 | 5 | module.exports = { 6 | accessory: GenericRS232DeviceAccessory 7 | } 8 | 9 | function GenericRS232DeviceAccessory(log, config) { 10 | this.log = log; 11 | this.id = config["id"]; 12 | this.name = config["name"]; 13 | this.model_name = config["model_name"]; 14 | this.manufacturer = config["manufacturer"]; 15 | this.on_command = config["on_command"]; 16 | this.off_command = config["off_command"]; 17 | this.device = config["device"]; 18 | this.baudrate = config["baudrate"]; 19 | } 20 | 21 | GenericRS232DeviceAccessory.prototype = { 22 | setPowerState: function(powerOn, callback) { 23 | var that = this; 24 | var command = powerOn ? that.on_command : that.off_command; 25 | var serialPort = new SerialPort(that.device, { baudrate: that.baudrate }, false); 26 | serialPort.open(function (error) { 27 | if (error) { 28 | callback(new Error('Can not communicate with ' + that.name + " (" + error + ")")) 29 | } else { 30 | serialPort.write(command, function(err, results) { 31 | if (error) { 32 | callback(new Error('Can not send power command to ' + that.name + " (" + err + ")")) 33 | } else { 34 | callback() 35 | } 36 | }); 37 | } 38 | }); 39 | }, 40 | 41 | getServices: function() { 42 | var switchService = new Service.Switch(this.name); 43 | var informationService = new Service.AccessoryInformation(); 44 | 45 | informationService 46 | .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) 47 | .setCharacteristic(Characteristic.Model, this.model_name) 48 | .setCharacteristic(Characteristic.SerialNumber, this.id); 49 | 50 | switchService 51 | .getCharacteristic(Characteristic.On) 52 | .on('set', this.setPowerState.bind(this)); 53 | 54 | return [informationService, switchService]; 55 | } 56 | } 57 | 58 | module.exports.accessory = GenericRS232DeviceAccessory; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | STEPS TO ACTIVATE SIRI TO CONTROL TESLA 2 | 3 | 4 | **Download My App from the App Store.** 5 | https://itunes.apple.com/us/app/energi-simple-remote-for-tesla/id1102475223?mt=8 6 | 7 | **VIDEO TUTORIAL** 8 | 9 | [![How To: Use Siri To Control Your Tesla](http://img.youtube.com/vi/bGa1qPmNAkI/maxresdefault.jpg)](https://www.youtube.com/watch?v=bGa1qPmNAkI) 10 | 11 | https://www.youtube.com/watch?v=bGa1qPmNAkI 12 | 13 | 14 | **XCODE MUST BE INSTALLED ON THE MAC BEFORE ANYTHING. 15 | YOU CAN DOWNLOAD THE LATEST VERSION OF XCODE FROM THE MAC APP STORE** 16 | 17 | **DOWNLOAD ATOM TEXT EDITOR TO EDIT ALL .json FILES at https://atom.io/download/mac DO NOT USE TEXT EDITOR ON THE MAC. IT WILL CORRUPT THE FILES** 18 | 19 | **STEP ONE: Install Node.js + NPM** 20 | 21 | 1. Download Node Installer from **https://nodejs.org/dist/v6.1.0/node-v6.1.0.pkg** 22 | 2. Follow the steps to install Node 23 | 24 | You should now have Node installed on your Mac. You can check by typing **node -v** in the Terminal Window. 25 | 26 | Go to https://github.com/SamGabbay/tesla-siri-homekit 27 | 28 | Download this file for later... 29 | https://www.sendspace.com/file/n0jhbw 30 | 31 | **STEP TWO: Logging into Tesla API** 32 | 33 | Now that NODE & NPM is installed on your Mac, you now have to enter your Tesla Username/Password into the **config.json** file in the main directory of the GitHub folder downloaded. You will need to replace every **USER_NAME** & **PASSWORD** with your Tesla Username & Password. 34 | 35 | **STEP THREE: Entering Codes Into Terminal** 36 | 37 | RUN THIS IN TERMINAL 38 | 39 | 1. **cd /PATH/OF/DOWNLOADED/GITHUB/FOLDER** 40 | 2. Add the folder from **https://www.sendspace.com/file/n0jhbw** into the /homebridge-legacy-plugins folder. 41 | 3. **npm install** 42 | 4. **DEBUG=* ./bin/homebridge -D -P ./homebridge-legacy-plugins** 43 | 44 | If all is successful, then you should see the following 031-45-154. 45 | 46 | **STEP FOUR: Setting Up The Energi HomeKit Accessory** 47 | 48 | To setup the Energi HomeKit Accessory that is currently being broadcasted from your Mac, you need to have an App that allows setup of HomeKit devices. This App worked for me. 49 | 50 | https://itunes.apple.com/us/app/ezzi-home-control-for-homekit/id1039845950?mt=8 51 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/HttpHygrometer.js: -------------------------------------------------------------------------------- 1 | var Service = require("../api").homebridge.hap.Service; 2 | var Characteristic = require("../api").homebridge.hap.Characteristic; 3 | var request = require("request"); 4 | 5 | module.exports = { 6 | accessory: HygrometerAccessory 7 | } 8 | 9 | function HygrometerAccessory(log, config) { 10 | this.log = log; 11 | 12 | // url info 13 | this.url = config["url"]; 14 | this.http_method = config["http_method"]; 15 | } 16 | 17 | 18 | HygrometerAccessory.prototype = { 19 | 20 | httpRequest: function(url, method, callback) { 21 | request({ 22 | url: url, 23 | method: method 24 | }, 25 | function (error, response, body) { 26 | callback(error, response, body) 27 | }) 28 | }, 29 | 30 | 31 | identify: function(callback) { 32 | this.log("Identify requested!"); 33 | callback(); // success 34 | }, 35 | 36 | getCurrentRelativeHumidity: function (callback) { 37 | var that = this; 38 | that.log ("getting CurrentCurrentRelativeHumidity"); 39 | 40 | this.httpRequest(this.url, this.http_method, function(error, response, body) { 41 | if (error) { 42 | this.log('HTTP function failed: %s', error); 43 | callback(error); 44 | } 45 | else { 46 | this.log('HTTP function succeeded - %s', body); 47 | callback(null, Number(body)); 48 | } 49 | }.bind(this)); 50 | }, 51 | 52 | getServices: function() { 53 | 54 | // you can OPTIONALLY create an information service if you wish to override 55 | // the default values for things like serial number, model, etc. 56 | var informationService = new Service.AccessoryInformation(); 57 | 58 | informationService 59 | .setCharacteristic(Characteristic.Manufacturer, "HTTP Manufacturer") 60 | .setCharacteristic(Characteristic.Model, "HTTP Hygrometer") 61 | .setCharacteristic(Characteristic.SerialNumber, "HTTP Serial Number"); 62 | 63 | var humidityService = new Service.HumiditySensor(); 64 | 65 | humidityService 66 | .getCharacteristic(Characteristic.CurrentRelativeHumidity) 67 | .on('get', this.getCurrentRelativeHumidity.bind(this)); 68 | 69 | return [informationService, humidityService]; 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/HttpThermometer.js: -------------------------------------------------------------------------------- 1 | var Service = require("../api").homebridge.hap.Service; 2 | var Characteristic = require("../api").homebridge.hap.Characteristic; 3 | var request = require("request"); 4 | 5 | module.exports = { 6 | accessory: ThermometerAccessory 7 | } 8 | 9 | function ThermometerAccessory(log, config) { 10 | this.log = log; 11 | 12 | // url info 13 | this.url = config["url"]; 14 | this.http_method = config["http_method"]; 15 | } 16 | 17 | 18 | ThermometerAccessory.prototype = { 19 | 20 | httpRequest: function(url, method, callback) { 21 | request({ 22 | url: url, 23 | method: method 24 | }, 25 | function (error, response, body) { 26 | callback(error, response, body) 27 | }) 28 | }, 29 | 30 | 31 | identify: function(callback) { 32 | this.log("Identify requested!"); 33 | callback(); // success 34 | }, 35 | 36 | getCurrentTemperature: function (callback) { 37 | var that = this; 38 | that.log ("getting CurrentTemperature"); 39 | 40 | 41 | this.httpRequest(this.url, this.http_method, function(error, response, body) { 42 | if (error) { 43 | this.log('HTTP function failed: %s', error); 44 | callback(error); 45 | } 46 | else { 47 | this.log('HTTP function succeeded - %s', body); 48 | callback(null, Number(body)); 49 | } 50 | }.bind(this)); 51 | }, 52 | 53 | getTemperatureUnits: function (callback) { 54 | var that = this; 55 | that.log ("getTemperature Units"); 56 | // 1 = F and 0 = C 57 | callback (null, 0); 58 | }, 59 | 60 | getServices: function() { 61 | 62 | // you can OPTIONALLY create an information service if you wish to override 63 | // the default values for things like serial number, model, etc. 64 | var informationService = new Service.AccessoryInformation(); 65 | 66 | informationService 67 | .setCharacteristic(Characteristic.Manufacturer, "HTTP Manufacturer") 68 | .setCharacteristic(Characteristic.Model, "HTTP Thermometer") 69 | .setCharacteristic(Characteristic.SerialNumber, "HTTP Serial Number"); 70 | 71 | var temperatureService = new Service.TemperatureSensor(); 72 | 73 | temperatureService 74 | .getCharacteristic(Characteristic.CurrentTemperature) 75 | .on('get', this.getCurrentTemperature.bind(this)); 76 | 77 | return [informationService, temperatureService]; 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/mpdclient.js: -------------------------------------------------------------------------------- 1 | var Service = require("../api").homebridge.hap.Service; 2 | var Characteristic = require("../api").homebridge.hap.Characteristic; 3 | var request = require("request"); 4 | var komponist = require('komponist') 5 | 6 | module.exports = { 7 | accessory: MpdClient 8 | } 9 | 10 | function MpdClient(log, config) { 11 | this.log = log; 12 | this.host = config["host"] || 'localhost'; 13 | this.port = config["port"] || 6600; 14 | } 15 | 16 | MpdClient.prototype = { 17 | 18 | setPowerState: function(powerOn, callback) { 19 | 20 | var log = this.log; 21 | 22 | komponist.createConnection(this.port, this.host, function(error, client) { 23 | 24 | if (error) { 25 | return callback(error); 26 | } 27 | 28 | if (powerOn) { 29 | client.play(function(error) { 30 | log("start playing"); 31 | client.destroy(); 32 | callback(error); 33 | }); 34 | } else { 35 | client.stop(function(error) { 36 | log("stop playing"); 37 | client.destroy(); 38 | callback(error); 39 | }); 40 | } 41 | 42 | }); 43 | }, 44 | 45 | getPowerState: function(callback) { 46 | 47 | komponist.createConnection(this.port, this.host, function(error, client) { 48 | 49 | if (error) { 50 | return callback(error); 51 | } 52 | 53 | client.status(function(error, status) { 54 | 55 | client.destroy(); 56 | 57 | if (status['state'] == 'play') { 58 | callback(error, 1); 59 | } else { 60 | callback(error, 0); 61 | } 62 | }); 63 | 64 | }); 65 | }, 66 | 67 | identify: function(callback) { 68 | this.log("Identify requested!"); 69 | callback(); // success 70 | }, 71 | 72 | getServices: function() { 73 | 74 | var informationService = new Service.AccessoryInformation(); 75 | 76 | informationService 77 | .setCharacteristic(Characteristic.Manufacturer, "MPD") 78 | .setCharacteristic(Characteristic.Model, "MPD Client") 79 | .setCharacteristic(Characteristic.SerialNumber, "81536334"); 80 | 81 | var switchService = new Service.Switch(); 82 | 83 | switchService.getCharacteristic(Characteristic.On) 84 | .on('get', this.getPowerState.bind(this)) 85 | .on('set', this.setPowerState.bind(this)); 86 | 87 | return [informationService, switchService]; 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Energi Bridge HomeKit", 4 | "username": "CC:22:3D:E3:CE:30", 5 | "port": 51826, 6 | "pin": "031-45-154" 7 | }, 8 | 9 | "description": "Description 29857349857", 10 | 11 | "accessories": [ 12 | { 13 | "accessory": "Tesla", 14 | "name": "Climate control", 15 | "description": "Start or stop 8925879348", 16 | "username": "USER_NAME", 17 | "password": "PASSWORD" 18 | }, 19 | { 20 | "accessory": "TeslaSunroof", 21 | "name": "Sunroof", 22 | "description": "Start or stop 8925879348", 23 | "username": "USER_NAME", 24 | "password": "PASSWORD" 25 | }, 26 | { 27 | "accessory": "TeslaUnlock", 28 | "name": "Tesla", 29 | "description": "Start or stop 8925879348", 30 | "username": "USER_NAME", 31 | "password": "PASSWORD" 32 | }, 33 | { 34 | "accessory": "TeslaSummon", 35 | "name": "Summon", 36 | "description": "Start or stop 8925879348", 37 | "username": "USER_NAME", 38 | "password": "PASSWORD" 39 | }, 40 | { 41 | "accessory": "TeslaSummonForward", 42 | "name": "Summon Forward", 43 | "description": "Start or stop 8925879348", 44 | "username": "USER_NAME", 45 | "password": "PASSWORD" 46 | }, 47 | { 48 | "accessory": "TeslaGarage", 49 | "name": "Garage", 50 | "description": "Start or stop 8925879348", 51 | "username": "USER_NAME", 52 | "password": "PASSWORD" 53 | }, 54 | { 55 | "accessory": "TeslaChargePort", 56 | "name": "Charge Port", 57 | "description": "Start or stop 8925879348", 58 | "username": "USER_NAME", 59 | "password": "PASSWORD" 60 | }, 61 | { 62 | "accessory": "TeslaHorn", 63 | "name": "Honk Horn", 64 | "description": "Start or stop 8925879348", 65 | "username": "USER_NAME", 66 | "password": "PASSWORD" 67 | }, 68 | { 69 | "accessory": "TeslaFlash", 70 | "name": "Flash Lights", 71 | "description": "Start or stop 8925879348", 72 | "username": "USER_NAME", 73 | "password": "PASSWORD" 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /lib/logger.js: -------------------------------------------------------------------------------- 1 | var chalk = require('chalk'); 2 | var util = require('util'); 3 | 4 | 'use strict'; 5 | 6 | module.exports = { 7 | Logger: Logger, 8 | setDebugEnabled: setDebugEnabled, 9 | _system: new Logger() // system logger, for internal use only 10 | } 11 | 12 | var DEBUG_ENABLED = false; 13 | 14 | // Turns on debug level logging 15 | function setDebugEnabled(enabled) { 16 | DEBUG_ENABLED = enabled; 17 | } 18 | 19 | // global cache of logger instances by plugin name 20 | var loggerCache = {}; 21 | 22 | /** 23 | * Logger class 24 | */ 25 | 26 | function Logger(prefix) { 27 | this.prefix = prefix; 28 | } 29 | 30 | Logger.prototype.debug = function(msg) { 31 | if (DEBUG_ENABLED) 32 | this.log.apply(this, ['debug'].concat(Array.prototype.slice.call(arguments))); 33 | } 34 | 35 | Logger.prototype.info = function(msg) { 36 | this.log.apply(this, ['info'].concat(Array.prototype.slice.call(arguments))); 37 | } 38 | 39 | Logger.prototype.warn = function(msg) { 40 | this.log.apply(this, ['warn'].concat(Array.prototype.slice.call(arguments))); 41 | } 42 | 43 | Logger.prototype.error = function(msg) { 44 | this.log.apply(this, ['error'].concat(Array.prototype.slice.call(arguments))); 45 | } 46 | 47 | Logger.prototype.log = function(level, msg) { 48 | 49 | msg = util.format.apply(util, Array.prototype.slice.call(arguments, 1)); 50 | func = console.log; 51 | 52 | if (level == 'debug') { 53 | msg = chalk.gray(msg); 54 | } 55 | else if (level == 'warn') { 56 | msg = chalk.yellow(msg); 57 | func = console.error; 58 | } 59 | else if (level == 'error') { 60 | msg = chalk.bold.red(msg); 61 | func = console.error; 62 | } 63 | 64 | // prepend prefix if applicable 65 | if (this.prefix) 66 | msg = chalk.cyan("[" + this.prefix + "]") + " " + msg; 67 | 68 | // prepend timestamp 69 | var date = new Date(); 70 | msg = "[" + date.toLocaleString() + "]" + " " + msg; 71 | 72 | func(msg); 73 | } 74 | 75 | Logger.withPrefix = function(prefix) { 76 | 77 | if (!loggerCache[prefix]) { 78 | // create a class-like logger thing that acts as a function as well 79 | // as an instance of Logger. 80 | var logger = new Logger(prefix); 81 | var log = logger.info.bind(logger); 82 | log.debug = logger.debug; 83 | log.info = logger.info; 84 | log.warn = logger.warn; 85 | log.error = logger.error; 86 | log.log = logger.log; 87 | log.prefix = logger.prefix; 88 | loggerCache[prefix] = log; 89 | } 90 | 91 | return loggerCache[prefix]; 92 | } 93 | -------------------------------------------------------------------------------- /lib/bridgeSetupManager.js: -------------------------------------------------------------------------------- 1 | var inherits = require('util').inherits; 2 | var EventEmitter = require('events').EventEmitter; 3 | var Service = require("hap-nodejs").Service; 4 | var Characteristic = require("hap-nodejs").Characteristic; 5 | var SetupSession = require("./bridgeSetupSession").SetupSession; 6 | 7 | 'use strict'; 8 | 9 | module.exports = { 10 | BridgeSetupManager: BridgeSetupManager 11 | } 12 | 13 | function BridgeSetupManager() { 14 | this.session; 15 | 16 | this.service = new Service(null, "49FB9D4D-0FEA-4BF1-8FA6-E7B18AB86DCE"); 17 | 18 | this.stateCharacteristic = new Characteristic("State", "77474A2F-FA98-485E-97BE-4762458774D8", { 19 | format: Characteristic.Formats.UINT8, 20 | minValue: 0, 21 | maxValue: 1, 22 | minStep: 1, 23 | perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY] 24 | }); 25 | this.stateCharacteristic.value = 0; 26 | this.service.addCharacteristic(this.stateCharacteristic); 27 | 28 | this.versionCharacteristic = new Characteristic("Version", "FD9FE4CC-D06F-4FFE-96C6-595D464E1026", { 29 | format: Characteristic.Formats.STRING, 30 | perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY] 31 | }); 32 | this.versionCharacteristic.value = "1.0"; 33 | this.service.addCharacteristic(this.versionCharacteristic); 34 | 35 | this.controlPointCharacteristic = new Characteristic("Control Point", "5819A4C2-E1B0-4C9D-B761-3EB1AFF43073", { 36 | format: Characteristic.Formats.DATA, 37 | perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY] 38 | }) 39 | this.controlPointCharacteristic.on('get', function(callback, context) { 40 | this.handleReadRequest(callback, context); 41 | }.bind(this)); 42 | this.controlPointCharacteristic.on('set', function(newValue, callback, context) { 43 | this.handleWriteRequest(newValue, callback, context); 44 | }.bind(this)); 45 | 46 | this.controlPointCharacteristic.value = null; 47 | this.service.addCharacteristic(this.controlPointCharacteristic); 48 | } 49 | 50 | inherits(BridgeSetupManager, EventEmitter); 51 | 52 | BridgeSetupManager.prototype.handleReadRequest = function(callback, context) { 53 | if (!context) { 54 | return; 55 | } 56 | 57 | if (!this.session) { 58 | callback(null, null); 59 | } else { 60 | this.session.handleReadRequest(callback); 61 | } 62 | } 63 | 64 | BridgeSetupManager.prototype.handleWriteRequest = function(value, callback, context) { 65 | if (!context) { 66 | callback(); 67 | return; 68 | } 69 | 70 | var data = new Buffer(value, 'base64'); 71 | var request = JSON.parse(data.toString()); 72 | callback(); 73 | 74 | if (!this.session || this.session.sessionUUID !== request.sid) { 75 | if (this.session) { 76 | this.session.removeAllListeners(); 77 | this.session.validSession = false; 78 | } 79 | 80 | this.session = new SetupSession(this.stateCharacteristic, this.controlPointCharacteristic); 81 | this.session.configurablePlatformPlugins = this.configurablePlatformPlugins; 82 | this.session.on('newConfig', function(type, name, replace, config) { 83 | this.emit('newConfig', type, name, replace, config); 84 | }.bind(this)); 85 | 86 | this.session.on('requestCurrentConfig', function(callback) { 87 | this.emit('requestCurrentConfig', callback); 88 | }.bind(this)); 89 | 90 | this.session.on('end', function() { 91 | this.session = null; 92 | }.bind(this)); 93 | } 94 | 95 | this.session.handleWriteRequest(request); 96 | } -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/ELKM1.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var elkington = require("elkington"); 3 | 4 | function ElkM1Accessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.zone = config["zone"]; 8 | this.host = config["host"]; 9 | this.port = config["port"]; 10 | this.pin = config["pin"]; 11 | this.arm = config["arm"]; 12 | } 13 | 14 | ElkM1Accessory.prototype = { 15 | setPowerState: function(alarmOn) { 16 | var that = this; 17 | 18 | if (!alarmOn) 19 | { 20 | return; 21 | } 22 | 23 | var elk = elkington.createConnection({ 24 | port: that.port, 25 | host: that.host, 26 | }); 27 | 28 | switch (that.arm) 29 | { 30 | case 'Away': 31 | elk.armAway({area: that.zone, code: that.pin}); 32 | break; 33 | case 'Stay': 34 | elk.armStay({area: that.zone, code: that.pin}); 35 | break; 36 | case 'Night': 37 | elk.armNightInstant({area: that.zone, code: that.pin}); 38 | break; 39 | default: 40 | break; 41 | } 42 | }, 43 | 44 | getServices: function() { 45 | var that = this; 46 | return [{ 47 | sType: types.ACCESSORY_INFORMATION_STYPE, 48 | characteristics: [{ 49 | cType: types.NAME_CTYPE, 50 | onUpdate: null, 51 | perms: ["pr"], 52 | format: "string", 53 | initialValue: this.name, 54 | supportEvents: false, 55 | supportBonjour: false, 56 | manfDescription: "Name of the accessory", 57 | designedMaxLength: 255 58 | },{ 59 | cType: types.MANUFACTURER_CTYPE, 60 | onUpdate: null, 61 | perms: ["pr"], 62 | format: "string", 63 | initialValue: "Elk", 64 | supportEvents: false, 65 | supportBonjour: false, 66 | manfDescription: "Manufacturer", 67 | designedMaxLength: 255 68 | },{ 69 | cType: types.MODEL_CTYPE, 70 | onUpdate: null, 71 | perms: ["pr"], 72 | format: "string", 73 | initialValue: "M1", 74 | supportEvents: false, 75 | supportBonjour: false, 76 | manfDescription: "Model", 77 | designedMaxLength: 255 78 | },{ 79 | cType: types.SERIAL_NUMBER_CTYPE, 80 | onUpdate: null, 81 | perms: ["pr"], 82 | format: "string", 83 | initialValue: "A1S2NASF88EW", 84 | supportEvents: false, 85 | supportBonjour: false, 86 | manfDescription: "SN", 87 | designedMaxLength: 255 88 | },{ 89 | cType: types.IDENTIFY_CTYPE, 90 | onUpdate: null, 91 | perms: ["pw"], 92 | format: "bool", 93 | initialValue: false, 94 | supportEvents: false, 95 | supportBonjour: false, 96 | manfDescription: "Identify Accessory", 97 | designedMaxLength: 1 98 | }] 99 | },{ 100 | sType: types.SWITCH_STYPE, 101 | characteristics: [{ 102 | cType: types.NAME_CTYPE, 103 | onUpdate: null, 104 | perms: ["pr"], 105 | format: "string", 106 | initialValue: this.name, 107 | supportEvents: false, 108 | supportBonjour: false, 109 | manfDescription: "Name of service", 110 | designedMaxLength: 255 111 | },{ 112 | cType: types.POWER_STATE_CTYPE, 113 | onUpdate: function(value) { that.setPowerState(value); }, 114 | perms: ["pw","pr","ev"], 115 | format: "bool", 116 | initialValue: false, 117 | supportEvents: false, 118 | supportBonjour: false, 119 | manfDescription: "Alarm the Zone", 120 | designedMaxLength: 1 121 | }] 122 | }]; 123 | } 124 | }; 125 | 126 | module.exports.accessory = ElkM1Accessory; 127 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/HttpGarageDoorOpener.js: -------------------------------------------------------------------------------- 1 | /* 2 | { 3 | "bridge": { 4 | "name": "Homebridge", 5 | "username": "CC:22:3D:E3:CE:30", 6 | "port": 51826, 7 | "pin": "031-45-154" 8 | }, 9 | 10 | "description": "This is an example configuration file with all supported devices. You can use this as a template for creating your own configuration file containing devices you actually own.", 11 | 12 | "platforms": [], 13 | "accessories": [ 14 | { 15 | "accessory": "HttpGarageDoorOpener", 16 | "name": "Porte de Garage", 17 | "description": "", 18 | "open_url": "http://0.0.0.0:3000", 19 | "http_method": "GET" 20 | } 21 | ] 22 | } 23 | */ 24 | 25 | var Service = require("../api").homebridge.hap.Service; 26 | var Characteristic = require("../api").homebridge.hap.Characteristic; 27 | 28 | var request = require("request"); 29 | 30 | module.exports = { 31 | accessory: HttpGarageDoorOpener 32 | } 33 | 34 | function HttpGarageDoorOpener(log, config) { 35 | this.log = log; 36 | this.open_url = config["open_url"]; 37 | this.http_method = config["http_method"]; 38 | this.garageDoorStatus = Characteristic.CurrentDoorState.CLOSED; 39 | } 40 | 41 | HttpGarageDoorOpener.prototype = { 42 | close: function (callback) { 43 | this.garageDoorStatus = Characteristic.CurrentDoorState.CLOSED; 44 | this.log("Door is", this.getCurrentDoorStateReadable()); 45 | callback(); 46 | }, 47 | 48 | open: function (callback) { 49 | this.garageDoorStatus = Characteristic.CurrentDoorState.OPEN; 50 | this.log("Door is", this.getCurrentDoorStateReadable()); 51 | callback(); 52 | }, 53 | 54 | identify: function() { 55 | console.log("Identify the Door!"); 56 | }, 57 | 58 | getServices: function () { 59 | this.garageDoorOpenerService = new Service.GarageDoorOpener(); 60 | 61 | this.garageDoorOpenerService 62 | .getCharacteristic(Characteristic.CurrentDoorState) 63 | .on('get', this.getCurrentDoorState.bind(this)); 64 | 65 | this.garageDoorOpenerService 66 | .getCharacteristic(Characteristic.TargetDoorState) 67 | .on('set', this.setTargetDoorState.bind(this)); 68 | 69 | /* 70 | garageDoorOpenerService 71 | .getCharacteristic(Characteristic.ObstructionDetected) 72 | .on('get', this.getObstructionDetected.bind(this)) 73 | .on('set', this.setObstructionDetected.bind(this)); 74 | */ 75 | 76 | var informationService = new Service.AccessoryInformation(); 77 | informationService 78 | .setCharacteristic(Characteristic.Manufacturer, "HTTP Manufacturer") 79 | .setCharacteristic(Characteristic.Model, "HTTP Model") 80 | .setCharacteristic(Characteristic.SerialNumber, "HTTP Serial Number"); 81 | 82 | return [informationService, this.garageDoorOpenerService]; 83 | }, 84 | 85 | getCurrentDoorStateReadable: function () { 86 | var textState = ""; 87 | switch (this.garageDoorStatus) { 88 | case 0: textState = "OPEN"; break; 89 | case 1: textState = "CLOSED"; break; 90 | case 2: textState = "OPENING"; break; 91 | case 3: textState = "CLOSING"; break; 92 | case 4: textState = "STOPPED"; break; 93 | default: this.log("Unhandled CurrentDoorState"); 94 | } 95 | return textState; 96 | }, 97 | 98 | getCurrentDoorState: function(callback) { 99 | 100 | this.log("The door is now", this.getCurrentDoorStateReadable() ,"("+ this.garageDoorStatus + ")"); 101 | 102 | var error = null; 103 | var returnValue = this.state; 104 | 105 | callback(null, returnValue); 106 | }, 107 | 108 | setTargetDoorState: function(value, callback) { 109 | if(value === Characteristic.TargetDoorState.OPEN) { 110 | this.open(callback); 111 | } else { 112 | this.close(callback); 113 | }; 114 | } 115 | }; -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/Carwings.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var carwings = require("carwingsjs"); 3 | 4 | function CarwingsAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | CarwingsAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | carwings.login(this.username, this.password, function(err, result) { 17 | if (!err) { 18 | that.vin = result.vin; 19 | that.log("Got VIN: " + that.vin); 20 | 21 | if (powerOn) { 22 | carwings.startClimateControl(that.vin, null, function(err, result) { 23 | if (!err) 24 | that.log("Started climate control."); 25 | else 26 | that.log("Error starting climate control: " + err); 27 | }); 28 | } 29 | else { 30 | carwings.stopClimateControl(that.vin, function(err, result) { 31 | if (!err) 32 | that.log("Stopped climate control."); 33 | else 34 | that.log("Error stopping climate control: " + err); 35 | }); 36 | } 37 | } 38 | else { 39 | that.log("Error logging in: " + err); 40 | } 41 | }); 42 | }, 43 | 44 | getServices: function() { 45 | var that = this; 46 | return [{ 47 | sType: types.ACCESSORY_INFORMATION_STYPE, 48 | characteristics: [{ 49 | cType: types.NAME_CTYPE, 50 | onUpdate: null, 51 | perms: ["pr"], 52 | format: "string", 53 | initialValue: this.name, 54 | supportEvents: false, 55 | supportBonjour: false, 56 | manfDescription: "Name of the accessory", 57 | designedMaxLength: 255 58 | },{ 59 | cType: types.MANUFACTURER_CTYPE, 60 | onUpdate: null, 61 | perms: ["pr"], 62 | format: "string", 63 | initialValue: "Nissan", 64 | supportEvents: false, 65 | supportBonjour: false, 66 | manfDescription: "Manufacturer", 67 | designedMaxLength: 255 68 | },{ 69 | cType: types.MODEL_CTYPE, 70 | onUpdate: null, 71 | perms: ["pr"], 72 | format: "string", 73 | initialValue: "Rev-1", 74 | supportEvents: false, 75 | supportBonjour: false, 76 | manfDescription: "Model", 77 | designedMaxLength: 255 78 | },{ 79 | cType: types.SERIAL_NUMBER_CTYPE, 80 | onUpdate: null, 81 | perms: ["pr"], 82 | format: "string", 83 | initialValue: "A1S2NASF88EW", 84 | supportEvents: false, 85 | supportBonjour: false, 86 | manfDescription: "SN", 87 | designedMaxLength: 255 88 | },{ 89 | cType: types.IDENTIFY_CTYPE, 90 | onUpdate: null, 91 | perms: ["pw"], 92 | format: "bool", 93 | initialValue: false, 94 | supportEvents: false, 95 | supportBonjour: false, 96 | manfDescription: "Identify Accessory", 97 | designedMaxLength: 1 98 | }] 99 | },{ 100 | sType: types.SWITCH_STYPE, 101 | characteristics: [{ 102 | cType: types.NAME_CTYPE, 103 | onUpdate: null, 104 | perms: ["pr"], 105 | format: "string", 106 | initialValue: this.name, 107 | supportEvents: false, 108 | supportBonjour: false, 109 | manfDescription: "Name of service", 110 | designedMaxLength: 255 111 | },{ 112 | cType: types.POWER_STATE_CTYPE, 113 | onUpdate: function(value) { that.setPowerState(value); }, 114 | perms: ["pw","pr","ev"], 115 | format: "bool", 116 | initialValue: false, 117 | supportEvents: false, 118 | supportBonjour: false, 119 | manfDescription: "Change the power state of the car", 120 | designedMaxLength: 1 121 | }] 122 | }]; 123 | } 124 | }; 125 | 126 | module.exports.accessory = CarwingsAccessory; 127 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/HomeMaticWindow.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var Characteristic = require("../api").homebridge.hap.Characteristic; 3 | var request = require("request"); 4 | 5 | function HomeMaticWindow(log, config) { 6 | this.log = log; 7 | this.name = config["name"]; 8 | this.ccuID = config["ccu_id"]; 9 | this.ccuIP = config["ccu_ip"]; 10 | } 11 | 12 | HomeMaticWindow.prototype = { 13 | 14 | 15 | getPowerState: function(callback) { 16 | var that = this; 17 | 18 | this.log("Getting Window State of CCU"); 19 | request.get({ 20 | url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuID, 21 | }, function(err, response, body) { 22 | 23 | if (!err && response.statusCode == 200) { 24 | 25 | //that.log("Response:"+response.body); 26 | var responseString = response.body.substring(83,84); 27 | //that.log(responseString); 28 | switch(responseString){ 29 | case "0": {callback(Characteristic.ContactSensorState.CONTACT_DETECTED);break;} 30 | case "1": {callback(Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);break;} 31 | case "2": {callback(Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);break;} 32 | } 33 | that.log("Getting Window State complete."); 34 | } 35 | else { 36 | that.log("Error '"+err+"' getting Window State: " + body); 37 | } 38 | }); 39 | }, 40 | 41 | getServices: function() { 42 | var that = this; 43 | return [{ 44 | sType: types.ACCESSORY_INFORMATION_STYPE, 45 | characteristics: [{ 46 | cType: types.NAME_CTYPE, 47 | onUpdate: null, 48 | perms: ["pr"], 49 | format: "string", 50 | initialValue: this.name, 51 | supportEvents: false, 52 | supportBonjour: false, 53 | manfDescription: "Name of the accessory", 54 | designedMaxLength: 255 55 | },{ 56 | cType: types.MANUFACTURER_CTYPE, 57 | onUpdate: null, 58 | perms: ["pr"], 59 | format: "string", 60 | initialValue: "Homematic", 61 | supportEvents: false, 62 | supportBonjour: false, 63 | manfDescription: "Manufacturer", 64 | designedMaxLength: 255 65 | },{ 66 | cType: types.MODEL_CTYPE, 67 | onUpdate: null, 68 | perms: ["pr"], 69 | format: "string", 70 | initialValue: "HM-Sec-RHS", 71 | supportEvents: false, 72 | supportBonjour: false, 73 | manfDescription: "Model", 74 | designedMaxLength: 255 75 | },{ 76 | cType: types.SERIAL_NUMBER_CTYPE, 77 | onUpdate: null, 78 | perms: ["pr"], 79 | format: "string", 80 | initialValue: "A1S2NASF88EW", 81 | supportEvents: false, 82 | supportBonjour: false, 83 | manfDescription: "SN", 84 | designedMaxLength: 255 85 | },{ 86 | cType: types.IDENTIFY_CTYPE, 87 | onUpdate: null, 88 | perms: ["pw"], 89 | format: "bool", 90 | initialValue: false, 91 | supportEvents: false, 92 | supportBonjour: false, 93 | manfDescription: "Identify Accessory", 94 | designedMaxLength: 1 95 | }] 96 | },{ 97 | sType: types.CONTACT_SENSOR_STYPE, 98 | characteristics: [{ 99 | cType: types.NAME_CTYPE, 100 | onUpdate: null, 101 | perms: ["pr"], 102 | format: "string", 103 | initialValue: this.name, 104 | supportEvents: false, 105 | supportBonjour: false, 106 | manfDescription: "Name of service", 107 | designedMaxLength: 255 108 | },{ 109 | cType: types.CONTACT_SENSOR_STATE_CTYPE, 110 | onRead: function(callback) { that.getPowerState(callback); }, 111 | perms: ["pr","ev"], 112 | format: "bool", 113 | initialValue: false, 114 | supportEvents: false, 115 | supportBonjour: false, 116 | manfDescription: "Get Window state of a Variable", 117 | designedMaxLength: 1 118 | }] 119 | }]; 120 | } 121 | }; 122 | 123 | module.exports.accessory = HomeMaticWindow; 124 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/TeslaHorn.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | 18 | if (powerOn) { 19 | tesla.honk({id:vehicle}, function(response) { 20 | if (response.result) 21 | that.log("Started Honking Horn."); 22 | else 23 | that.log("Error Honking Horn: " + response.reason); 24 | }); 25 | } 26 | else { 27 | tesla.honk({id:vehicle}, function(response) { 28 | if (response.result) 29 | that.log("Stopped Honking Horn."); 30 | else 31 | that.log("Error Honking Horn: " + response.reason); 32 | }); 33 | } 34 | }) 35 | }, 36 | 37 | getServices: function() { 38 | var that = this; 39 | return [{ 40 | sType: types.ACCESSORY_INFORMATION_STYPE, 41 | characteristics: [{ 42 | cType: types.NAME_CTYPE, 43 | onUpdate: null, 44 | perms: ["pr"], 45 | format: "string", 46 | initialValue: this.name, 47 | supportEvents: false, 48 | supportBonjour: false, 49 | manfDescription: "Name of the accessory", 50 | designedMaxLength: 255 51 | },{ 52 | cType: types.MANUFACTURER_CTYPE, 53 | onUpdate: null, 54 | perms: ["pr"], 55 | format: "string", 56 | initialValue: "Tesla Horn", 57 | supportEvents: false, 58 | supportBonjour: false, 59 | manfDescription: "Manufacturer", 60 | designedMaxLength: 255 61 | },{ 62 | cType: types.MODEL_CTYPE, 63 | onUpdate: null, 64 | perms: ["pr"], 65 | format: "string", 66 | initialValue: "Rev-1", 67 | supportEvents: false, 68 | supportBonjour: false, 69 | manfDescription: "Model", 70 | designedMaxLength: 255 71 | },{ 72 | cType: types.SERIAL_NUMBER_CTYPE, 73 | onUpdate: null, 74 | perms: ["pr"], 75 | format: "string", 76 | initialValue: "A1S2NASF88EW", 77 | supportEvents: false, 78 | supportBonjour: false, 79 | manfDescription: "SN", 80 | designedMaxLength: 255 81 | },{ 82 | cType: types.IDENTIFY_CTYPE, 83 | onUpdate: null, 84 | perms: ["pw"], 85 | format: "bool", 86 | initialValue: false, 87 | supportEvents: false, 88 | supportBonjour: false, 89 | manfDescription: "Identify Accessory", 90 | designedMaxLength: 1 91 | }] 92 | },{ 93 | sType: types.SWITCH_STYPE, 94 | characteristics: [{ 95 | cType: types.NAME_CTYPE, 96 | onUpdate: null, 97 | perms: ["pr"], 98 | format: "string", 99 | initialValue: this.name, 100 | supportEvents: false, 101 | supportBonjour: false, 102 | manfDescription: "Name of service", 103 | designedMaxLength: 255 104 | },{ 105 | cType: types.POWER_STATE_CTYPE, 106 | onUpdate: function(value) { that.setPowerState(value); }, 107 | perms: ["pw","pr","ev"], 108 | format: "bool", 109 | initialValue: false, 110 | supportEvents: false, 111 | supportBonjour: false, 112 | manfDescription: "Honk the Horn", 113 | designedMaxLength: 1 114 | }] 115 | }]; 116 | } 117 | }; 118 | 119 | module.exports.accessory = TeslaAccessory; 120 | 121 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/TeslaFlash.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | 18 | if (powerOn) { 19 | tesla.flash({id:vehicle}, function(response) { 20 | if (response.result) 21 | that.log("Started Flashing Lights"); 22 | else 23 | that.log("Error Flashing Lights: " + response.reason); 24 | }); 25 | } 26 | else { 27 | tesla.flash({id:vehicle}, function(response) { 28 | if (response.result) 29 | that.log("Stopped Flashing Lights."); 30 | else 31 | that.log("Error Flashing Lights: " + response.reason); 32 | }); 33 | } 34 | }) 35 | }, 36 | 37 | getServices: function() { 38 | var that = this; 39 | return [{ 40 | sType: types.ACCESSORY_INFORMATION_STYPE, 41 | characteristics: [{ 42 | cType: types.NAME_CTYPE, 43 | onUpdate: null, 44 | perms: ["pr"], 45 | format: "string", 46 | initialValue: this.name, 47 | supportEvents: false, 48 | supportBonjour: false, 49 | manfDescription: "Name of the accessory", 50 | designedMaxLength: 255 51 | },{ 52 | cType: types.MANUFACTURER_CTYPE, 53 | onUpdate: null, 54 | perms: ["pr"], 55 | format: "string", 56 | initialValue: "Tesla Horn", 57 | supportEvents: false, 58 | supportBonjour: false, 59 | manfDescription: "Manufacturer", 60 | designedMaxLength: 255 61 | },{ 62 | cType: types.MODEL_CTYPE, 63 | onUpdate: null, 64 | perms: ["pr"], 65 | format: "string", 66 | initialValue: "Rev-1", 67 | supportEvents: false, 68 | supportBonjour: false, 69 | manfDescription: "Model", 70 | designedMaxLength: 255 71 | },{ 72 | cType: types.SERIAL_NUMBER_CTYPE, 73 | onUpdate: null, 74 | perms: ["pr"], 75 | format: "string", 76 | initialValue: "A1S2NASF88EW", 77 | supportEvents: false, 78 | supportBonjour: false, 79 | manfDescription: "SN", 80 | designedMaxLength: 255 81 | },{ 82 | cType: types.IDENTIFY_CTYPE, 83 | onUpdate: null, 84 | perms: ["pw"], 85 | format: "bool", 86 | initialValue: false, 87 | supportEvents: false, 88 | supportBonjour: false, 89 | manfDescription: "Identify Accessory", 90 | designedMaxLength: 1 91 | }] 92 | },{ 93 | sType: types.SWITCH_STYPE, 94 | characteristics: [{ 95 | cType: types.NAME_CTYPE, 96 | onUpdate: null, 97 | perms: ["pr"], 98 | format: "string", 99 | initialValue: this.name, 100 | supportEvents: false, 101 | supportBonjour: false, 102 | manfDescription: "Name of service", 103 | designedMaxLength: 255 104 | },{ 105 | cType: types.POWER_STATE_CTYPE, 106 | onUpdate: function(value) { that.setPowerState(value); }, 107 | perms: ["pw","pr","ev"], 108 | format: "bool", 109 | initialValue: false, 110 | supportEvents: false, 111 | supportBonjour: false, 112 | manfDescription: "Honk the Horn", 113 | designedMaxLength: 1 114 | }] 115 | }]; 116 | } 117 | }; 118 | 119 | module.exports.accessory = TeslaAccessory; 120 | 121 | 122 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/Tesla.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | 18 | if (powerOn) { 19 | tesla.auto_conditioning({id:vehicle, climate: 'start'}, function(response) { 20 | if (response.result) 21 | that.log("Started climate control."); 22 | else 23 | that.log("Error starting climate control: " + response.reason); 24 | }); 25 | } 26 | else { 27 | tesla.auto_conditioning({id:vehicle, climate: 'stop'}, function(response) { 28 | if (response.result) 29 | that.log("Stopped climate control."); 30 | else 31 | that.log("Error stopping climate control: " + response.reason); 32 | }); 33 | } 34 | }) 35 | }, 36 | 37 | getServices: function() { 38 | var that = this; 39 | return [{ 40 | sType: types.ACCESSORY_INFORMATION_STYPE, 41 | characteristics: [{ 42 | cType: types.NAME_CTYPE, 43 | onUpdate: null, 44 | perms: ["pr"], 45 | format: "string", 46 | initialValue: this.name, 47 | supportEvents: false, 48 | supportBonjour: false, 49 | manfDescription: "Name of the accessory", 50 | designedMaxLength: 255 51 | },{ 52 | cType: types.MANUFACTURER_CTYPE, 53 | onUpdate: null, 54 | perms: ["pr"], 55 | format: "string", 56 | initialValue: "Tesla", 57 | supportEvents: false, 58 | supportBonjour: false, 59 | manfDescription: "Manufacturer", 60 | designedMaxLength: 255 61 | },{ 62 | cType: types.MODEL_CTYPE, 63 | onUpdate: null, 64 | perms: ["pr"], 65 | format: "string", 66 | initialValue: "Rev-1", 67 | supportEvents: false, 68 | supportBonjour: false, 69 | manfDescription: "Model", 70 | designedMaxLength: 255 71 | },{ 72 | cType: types.SERIAL_NUMBER_CTYPE, 73 | onUpdate: null, 74 | perms: ["pr"], 75 | format: "string", 76 | initialValue: "A1S2NASF88EW", 77 | supportEvents: false, 78 | supportBonjour: false, 79 | manfDescription: "SN", 80 | designedMaxLength: 255 81 | },{ 82 | cType: types.IDENTIFY_CTYPE, 83 | onUpdate: null, 84 | perms: ["pw"], 85 | format: "bool", 86 | initialValue: false, 87 | supportEvents: false, 88 | supportBonjour: false, 89 | manfDescription: "Identify Accessory", 90 | designedMaxLength: 1 91 | }] 92 | },{ 93 | sType: types.SWITCH_STYPE, 94 | characteristics: [{ 95 | cType: types.NAME_CTYPE, 96 | onUpdate: null, 97 | perms: ["pr"], 98 | format: "string", 99 | initialValue: this.name, 100 | supportEvents: false, 101 | supportBonjour: false, 102 | manfDescription: "Name of service", 103 | designedMaxLength: 255 104 | },{ 105 | cType: types.POWER_STATE_CTYPE, 106 | onUpdate: function(value) { that.setPowerState(value); }, 107 | perms: ["pw","pr","ev"], 108 | format: "bool", 109 | initialValue: false, 110 | supportEvents: false, 111 | supportBonjour: false, 112 | manfDescription: "Change the power state of the car", 113 | designedMaxLength: 1 114 | }] 115 | }]; 116 | } 117 | }; 118 | 119 | module.exports.accessory = TeslaAccessory; 120 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/HomeMatic.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var request = require("request"); 3 | 4 | function HomeMatic(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.ccuID = config["ccu_id"]; 8 | this.ccuIP = config["ccu_ip"]; 9 | } 10 | 11 | HomeMatic.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | 15 | var binaryState = powerOn ? 1 : 0; 16 | var that = this; 17 | 18 | this.log("Setting power state of CCU to " + powerOn); 19 | this.log(this.ccuID+ powerOn); 20 | 21 | request.put({ 22 | url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+this.ccuID+"&new_value="+ powerOn, 23 | }, function(err, response, body) { 24 | 25 | if (!err && response.statusCode == 200) { 26 | that.log("State change complete."); 27 | } 28 | else { 29 | that.log("Error '"+err+"' setting lock state: " + body); 30 | } 31 | }); 32 | }, 33 | getPowerState: function(callback) { 34 | var that = this; 35 | 36 | this.log("Getting Power State of CCU"); 37 | request.get({ 38 | url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuID, 39 | }, function(err, response, body) { 40 | 41 | if (!err && response.statusCode == 200) { 42 | 43 | //that.log("Response:"+response.body); 44 | var responseString = response.body.substring(83,87); 45 | //that.log(responseString); 46 | switch(responseString){ 47 | case "true": {modvalue = "1";break;} 48 | case "fals": {modvalue = "0";break;} 49 | } 50 | callback(parseInt(modvalue)); 51 | that.log("Getting Power State complete."); 52 | } 53 | else { 54 | that.log("Error '"+err+"' getting Power State: " + body); 55 | } 56 | }); 57 | }, 58 | getServices: function() { 59 | var that = this; 60 | return [{ 61 | sType: types.ACCESSORY_INFORMATION_STYPE, 62 | characteristics: [{ 63 | cType: types.NAME_CTYPE, 64 | onUpdate: null, 65 | perms: ["pr"], 66 | format: "string", 67 | initialValue: this.name, 68 | supportEvents: false, 69 | supportBonjour: false, 70 | manfDescription: "Name of the accessory", 71 | designedMaxLength: 255 72 | },{ 73 | cType: types.MANUFACTURER_CTYPE, 74 | onUpdate: null, 75 | perms: ["pr"], 76 | format: "string", 77 | initialValue: "WeMo", 78 | supportEvents: false, 79 | supportBonjour: false, 80 | manfDescription: "Manufacturer", 81 | designedMaxLength: 255 82 | },{ 83 | cType: types.MODEL_CTYPE, 84 | onUpdate: null, 85 | perms: ["pr"], 86 | format: "string", 87 | initialValue: "Rev-1", 88 | supportEvents: false, 89 | supportBonjour: false, 90 | manfDescription: "Model", 91 | designedMaxLength: 255 92 | },{ 93 | cType: types.SERIAL_NUMBER_CTYPE, 94 | onUpdate: null, 95 | perms: ["pr"], 96 | format: "string", 97 | initialValue: "A1S2NASF88EW", 98 | supportEvents: false, 99 | supportBonjour: false, 100 | manfDescription: "SN", 101 | designedMaxLength: 255 102 | },{ 103 | cType: types.IDENTIFY_CTYPE, 104 | onUpdate: null, 105 | perms: ["pw"], 106 | format: "bool", 107 | initialValue: false, 108 | supportEvents: false, 109 | supportBonjour: false, 110 | manfDescription: "Identify Accessory", 111 | designedMaxLength: 1 112 | }] 113 | },{ 114 | sType: types.SWITCH_STYPE, 115 | characteristics: [{ 116 | cType: types.NAME_CTYPE, 117 | onUpdate: null, 118 | perms: ["pr"], 119 | format: "string", 120 | initialValue: this.name, 121 | supportEvents: false, 122 | supportBonjour: false, 123 | manfDescription: "Name of service", 124 | designedMaxLength: 255 125 | },{ 126 | cType: types.POWER_STATE_CTYPE, 127 | onUpdate: function(value) { that.setPowerState(value); }, 128 | onRead: function(callback) { that.getPowerState(callback); }, 129 | perms: ["pw","pr","ev"], 130 | format: "bool", 131 | initialValue: false, 132 | supportEvents: false, 133 | supportBonjour: false, 134 | manfDescription: "Change the power state of a Variable", 135 | designedMaxLength: 1 136 | }] 137 | }]; 138 | } 139 | }; 140 | 141 | module.exports.accessory = HomeMatic; 142 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/TeslaSummon.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | 18 | if (powerOn) { 19 | tesla.autopark_request({id:vehicle, latitude: '40.282517', longitude:'-74.029605'}, function(response) { 20 | if (response.result) 21 | that.log("Started autopark request."); 22 | else 23 | that.log("Error starting autopark request: " + response.reason); 24 | }); 25 | } 26 | else { 27 | tesla.autopark_request({id:vehicle, latitude: '40.282517', longitude:'-74.029605'}, function(response) { 28 | if (response.result) 29 | that.log("Stopped autopark request."); 30 | else 31 | that.log("Error stopping autopark request: " + response.reason); 32 | }); 33 | } 34 | }) 35 | }, 36 | 37 | getServices: function() { 38 | var that = this; 39 | return [{ 40 | sType: types.ACCESSORY_INFORMATION_STYPE, 41 | characteristics: [{ 42 | cType: types.NAME_CTYPE, 43 | onUpdate: null, 44 | perms: ["pr"], 45 | format: "string", 46 | initialValue: this.name, 47 | supportEvents: false, 48 | supportBonjour: false, 49 | manfDescription: "Name of the accessory", 50 | designedMaxLength: 255 51 | },{ 52 | cType: types.MANUFACTURER_CTYPE, 53 | onUpdate: null, 54 | perms: ["pr"], 55 | format: "string", 56 | initialValue: "Tesla", 57 | supportEvents: false, 58 | supportBonjour: false, 59 | manfDescription: "Manufacturer", 60 | designedMaxLength: 255 61 | },{ 62 | cType: types.MODEL_CTYPE, 63 | onUpdate: null, 64 | perms: ["pr"], 65 | format: "string", 66 | initialValue: "Rev-1", 67 | supportEvents: false, 68 | supportBonjour: false, 69 | manfDescription: "Model", 70 | designedMaxLength: 255 71 | },{ 72 | cType: types.SERIAL_NUMBER_CTYPE, 73 | onUpdate: null, 74 | perms: ["pr"], 75 | format: "string", 76 | initialValue: "A1S2NASF88EW", 77 | supportEvents: false, 78 | supportBonjour: false, 79 | manfDescription: "SN", 80 | designedMaxLength: 255 81 | },{ 82 | cType: types.IDENTIFY_CTYPE, 83 | onUpdate: null, 84 | perms: ["pw"], 85 | format: "bool", 86 | initialValue: false, 87 | supportEvents: false, 88 | supportBonjour: false, 89 | manfDescription: "Identify Accessory", 90 | designedMaxLength: 1 91 | }] 92 | },{ 93 | sType: types.SWITCH_STYPE, 94 | characteristics: [{ 95 | cType: types.NAME_CTYPE, 96 | onUpdate: null, 97 | perms: ["pr"], 98 | format: "string", 99 | initialValue: this.name, 100 | supportEvents: false, 101 | supportBonjour: false, 102 | manfDescription: "Name of service", 103 | designedMaxLength: 255 104 | },{ 105 | cType: types.POWER_STATE_CTYPE, 106 | onUpdate: function(value) { that.setPowerState(value); }, 107 | perms: ["pw","pr","ev"], 108 | format: "bool", 109 | initialValue: false, 110 | supportEvents: false, 111 | supportBonjour: false, 112 | manfDescription: "Change the power state of the car", 113 | designedMaxLength: 1 114 | }] 115 | }]; 116 | } 117 | }; 118 | 119 | module.exports.accessory = TeslaAccessory; 120 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/TeslaSummonForward.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | 18 | if (powerOn) { 19 | tesla.autopark_request_forward({id:vehicle, latitude: '40.282517', longitude:'-74.029605'}, function(response) { 20 | if (response.result) 21 | that.log("Started autopark request."); 22 | else 23 | that.log("Error starting autopark request: " + response.reason); 24 | }); 25 | } 26 | else { 27 | tesla.autopark_request_forward({id:vehicle, latitude: '40.282517', longitude:'-74.029605'}, function(response) { 28 | if (response.result) 29 | that.log("Stopped autopark request."); 30 | else 31 | that.log("Error stopping autopark request: " + response.reason); 32 | }); 33 | } 34 | }) 35 | }, 36 | 37 | getServices: function() { 38 | var that = this; 39 | return [{ 40 | sType: types.ACCESSORY_INFORMATION_STYPE, 41 | characteristics: [{ 42 | cType: types.NAME_CTYPE, 43 | onUpdate: null, 44 | perms: ["pr"], 45 | format: "string", 46 | initialValue: this.name, 47 | supportEvents: false, 48 | supportBonjour: false, 49 | manfDescription: "Name of the accessory", 50 | designedMaxLength: 255 51 | },{ 52 | cType: types.MANUFACTURER_CTYPE, 53 | onUpdate: null, 54 | perms: ["pr"], 55 | format: "string", 56 | initialValue: "Tesla", 57 | supportEvents: false, 58 | supportBonjour: false, 59 | manfDescription: "Manufacturer", 60 | designedMaxLength: 255 61 | },{ 62 | cType: types.MODEL_CTYPE, 63 | onUpdate: null, 64 | perms: ["pr"], 65 | format: "string", 66 | initialValue: "Rev-1", 67 | supportEvents: false, 68 | supportBonjour: false, 69 | manfDescription: "Model", 70 | designedMaxLength: 255 71 | },{ 72 | cType: types.SERIAL_NUMBER_CTYPE, 73 | onUpdate: null, 74 | perms: ["pr"], 75 | format: "string", 76 | initialValue: "A1S2NASF88EW", 77 | supportEvents: false, 78 | supportBonjour: false, 79 | manfDescription: "SN", 80 | designedMaxLength: 255 81 | },{ 82 | cType: types.IDENTIFY_CTYPE, 83 | onUpdate: null, 84 | perms: ["pw"], 85 | format: "bool", 86 | initialValue: false, 87 | supportEvents: false, 88 | supportBonjour: false, 89 | manfDescription: "Identify Accessory", 90 | designedMaxLength: 1 91 | }] 92 | },{ 93 | sType: types.SWITCH_STYPE, 94 | characteristics: [{ 95 | cType: types.NAME_CTYPE, 96 | onUpdate: null, 97 | perms: ["pr"], 98 | format: "string", 99 | initialValue: this.name, 100 | supportEvents: false, 101 | supportBonjour: false, 102 | manfDescription: "Name of service", 103 | designedMaxLength: 255 104 | },{ 105 | cType: types.POWER_STATE_CTYPE, 106 | onUpdate: function(value) { that.setPowerState(value); }, 107 | perms: ["pw","pr","ev"], 108 | format: "bool", 109 | initialValue: false, 110 | supportEvents: false, 111 | supportBonjour: false, 112 | manfDescription: "Change the power state of the car", 113 | designedMaxLength: 1 114 | }] 115 | }]; 116 | } 117 | }; 118 | 119 | module.exports.accessory = TeslaAccessory; 120 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/X10.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var request = require("request"); 3 | 4 | function X10(log, config) { 5 | this.log = log; 6 | this.ip_address = config["ip_address"]; 7 | this.name = config["name"]; 8 | this.deviceID = config["device_id"]; 9 | this.protocol = config["protocol"]; 10 | this.canDim = config["can_dim"]; 11 | } 12 | 13 | X10.prototype = { 14 | 15 | setPowerState: function(powerOn) { 16 | 17 | var binaryState = powerOn ? "on" : "off"; 18 | var that = this; 19 | 20 | this.log("Setting power state of " + this.deviceID + " to " + powerOn); 21 | request.put({ 22 | url: "http://"+this.ip_address+"/x10/"+this.deviceID+"/power/"+binaryState+"?protocol="+this.protocol, 23 | }, function(err, response, body) { 24 | 25 | if (!err && response.statusCode == 200) { 26 | that.log("State change complete."); 27 | } 28 | else { 29 | that.log("Error '"+err+"' setting power state: " + body); 30 | } 31 | }); 32 | }, 33 | 34 | setBrightnessLevel: function(value) { 35 | 36 | var that = this; 37 | 38 | this.log("Setting brightness level of " + this.deviceID + " to " + value); 39 | request.put({ 40 | url: "http://"+this.ip_address+"/x10/"+this.deviceID+"/brightness/?protocol="+this.protocol+"&value="+value, 41 | }, function(err, response, body) { 42 | 43 | if (!err && response.statusCode == 200) { 44 | that.log("State change complete."); 45 | } 46 | else { 47 | that.log("Error '"+err+"' setting brightness level: " + body); 48 | } 49 | }); 50 | }, 51 | 52 | getServices: function() { 53 | var that = this; 54 | var services = [{ 55 | sType: types.ACCESSORY_INFORMATION_STYPE, 56 | characteristics: [{ 57 | cType: types.NAME_CTYPE, 58 | onUpdate: null, 59 | perms: ["pr"], 60 | format: "string", 61 | initialValue: this.name, 62 | supportEvents: false, 63 | supportBonjour: false, 64 | manfDescription: "Name of the accessory", 65 | designedMaxLength: 255 66 | },{ 67 | cType: types.MANUFACTURER_CTYPE, 68 | onUpdate: null, 69 | perms: ["pr"], 70 | format: "string", 71 | initialValue: "X10", 72 | supportEvents: false, 73 | supportBonjour: false, 74 | manfDescription: "Manufacturer", 75 | designedMaxLength: 255 76 | },{ 77 | cType: types.MODEL_CTYPE, 78 | onUpdate: null, 79 | perms: ["pr"], 80 | format: "string", 81 | initialValue: "Rev-1", 82 | supportEvents: false, 83 | supportBonjour: false, 84 | manfDescription: "Model", 85 | designedMaxLength: 255 86 | },{ 87 | cType: types.SERIAL_NUMBER_CTYPE, 88 | onUpdate: null, 89 | perms: ["pr"], 90 | format: "string", 91 | initialValue: "A1S2NASF88EW", 92 | supportEvents: false, 93 | supportBonjour: false, 94 | manfDescription: "SN", 95 | designedMaxLength: 255 96 | },{ 97 | cType: types.IDENTIFY_CTYPE, 98 | onUpdate: null, 99 | perms: ["pw"], 100 | format: "bool", 101 | initialValue: false, 102 | supportEvents: false, 103 | supportBonjour: false, 104 | manfDescription: "Identify Accessory", 105 | designedMaxLength: 1 106 | }] 107 | },{ 108 | sType: types.LIGHTBULB_STYPE, 109 | characteristics: [{ 110 | cType: types.NAME_CTYPE, 111 | onUpdate: null, 112 | perms: ["pr"], 113 | format: "string", 114 | initialValue: this.name, 115 | supportEvents: false, 116 | supportBonjour: false, 117 | manfDescription: "Name of service", 118 | designedMaxLength: 255 119 | },{ 120 | cType: types.POWER_STATE_CTYPE, 121 | onUpdate: function(value) { that.setPowerState(value); }, 122 | perms: ["pw","pr","ev"], 123 | format: "bool", 124 | initialValue: false, 125 | supportEvents: false, 126 | supportBonjour: false, 127 | manfDescription: "Change the power state of a Variable", 128 | designedMaxLength: 1 129 | }] 130 | }]; 131 | if (that.canDim) { 132 | services[1].characteristics.push({ 133 | cType: types.BRIGHTNESS_CTYPE, 134 | onUpdate: function(value) { that.setBrightnessLevel(value); }, 135 | perms: ["pw","pr","ev"], 136 | format: "int", 137 | initialValue: 0, 138 | supportEvents: false, 139 | supportBonjour: false, 140 | manfDescription: "Adjust Brightness of Light", 141 | designedMinValue: 0, 142 | designedMaxValue: 100, 143 | designedMinStep: 1, 144 | unit: "%" 145 | }); 146 | } 147 | return services; 148 | } 149 | }; 150 | 151 | module.exports.accessory = X10; 152 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/TeslaUnlock.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | /* 18 | var ROOF_CLOSE = 0; 19 | var ROOF_VENT = 1; 20 | var ROOF_COMFORT = 2; 21 | var ROOF_OPEN = 3; 22 | */ 23 | 24 | if (powerOn) { 25 | tesla.door_lock({id:vehicle, lock: 1}, function(response) { 26 | if (response.result) 27 | that.log("Starting to Locking Car."); 28 | else 29 | that.log("Error Locking Car: " + response.reason); 30 | }); 31 | } 32 | else { 33 | tesla.door_lock({id:vehicle, lock: 0}, function(response) { 34 | if (response.result) 35 | that.log("Starting to Unlock Car."); 36 | else 37 | that.log("Error Unlocking Car: " + response.reason); 38 | }); 39 | } 40 | }) 41 | }, 42 | 43 | getServices: function() { 44 | var that = this; 45 | return [{ 46 | sType: types.ACCESSORY_INFORMATION_STYPE, 47 | characteristics: [{ 48 | cType: types.NAME_CTYPE, 49 | onUpdate: null, 50 | perms: ["pr"], 51 | format: "string", 52 | initialValue: this.name, 53 | supportEvents: false, 54 | supportBonjour: false, 55 | manfDescription: "Name of the accessory", 56 | designedMaxLength: 255 57 | },{ 58 | cType: types.MANUFACTURER_CTYPE, 59 | onUpdate: null, 60 | perms: ["pr"], 61 | format: "string", 62 | initialValue: "Tesla", 63 | supportEvents: false, 64 | supportBonjour: false, 65 | manfDescription: "Manufacturer", 66 | designedMaxLength: 255 67 | },{ 68 | cType: types.MODEL_CTYPE, 69 | onUpdate: null, 70 | perms: ["pr"], 71 | format: "string", 72 | initialValue: "Rev-1", 73 | supportEvents: false, 74 | supportBonjour: false, 75 | manfDescription: "Model", 76 | designedMaxLength: 255 77 | },{ 78 | cType: types.SERIAL_NUMBER_CTYPE, 79 | onUpdate: null, 80 | perms: ["pr"], 81 | format: "string", 82 | initialValue: "A1S2NA45345SF88EW", 83 | supportEvents: false, 84 | supportBonjour: false, 85 | manfDescription: "SN", 86 | designedMaxLength: 255 87 | },{ 88 | cType: types.IDENTIFY_CTYPE, 89 | onUpdate: null, 90 | perms: ["pw"], 91 | format: "bool", 92 | initialValue: false, 93 | supportEvents: false, 94 | supportBonjour: false, 95 | manfDescription: "Identify Accessory", 96 | designedMaxLength: 1 97 | }] 98 | },{ 99 | sType: types.LOCK_MECHANISM_STYPE, 100 | characteristics: [{ 101 | cType: types.NAME_CTYPE, 102 | onUpdate: null, 103 | perms: ["pr"], 104 | format: "string", 105 | initialValue: this.name, 106 | supportEvents: false, 107 | supportBonjour: false, 108 | manfDescription: "Name of service", 109 | designedMaxLength: 255 110 | },{ 111 | cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, 112 | onUpdate: function(value) { that.setPowerState(value); }, 113 | perms: ["pr","ev"], 114 | format: "int", 115 | initialValue: 0, 116 | supportEvents: false, 117 | supportBonjour: false, 118 | manfDescription: "BlaBla", 119 | designedMinValue: 0, 120 | designedMaxValue: 3, 121 | designedMinStep: 1, 122 | designedMaxLength: 1 123 | },{ 124 | cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE, 125 | onUpdate: function(value) { 126 | console.log(that.name + "...changing status...."); 127 | that.setPowerState(value); 128 | }, 129 | perms: ["pr","pw","ev"], 130 | format: "int", 131 | initialValue: 0, 132 | supportEvents: false, 133 | supportBonjour: false, 134 | manfDescription: "BlaBla", 135 | designedMinValue: 0, 136 | designedMaxValue: 1, 137 | designedMinStep: 1, 138 | designedMaxLength: 1 139 | }] 140 | }]; 141 | } 142 | }; 143 | 144 | module.exports.accessory = TeslaAccessory; 145 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/TeslaSunroof.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | /* 18 | var ROOF_CLOSE = 0; 19 | var ROOF_VENT = 1; 20 | var ROOF_COMFORT = 2; 21 | var ROOF_OPEN = 3; 22 | */ 23 | 24 | if (powerOn) { 25 | tesla.open_sunroof({id:vehicle, roof: 0}, function(response) { 26 | if (response.result) 27 | that.log("Starting to Close Sunroof"); 28 | 29 | else 30 | that.log("Error Closing Sunroof: " + response.reason); 31 | }); 32 | } 33 | else { 34 | tesla.open_sunroof({id:vehicle, roof: 3}, function(response) { 35 | if (response.result) 36 | that.log("Starting to Open Sunroof."); 37 | else 38 | that.log("Error Opening Sunroof: " + response.reason); 39 | }); 40 | } 41 | }) 42 | }, 43 | 44 | getServices: function() { 45 | var that = this; 46 | return [{ 47 | sType: types.ACCESSORY_INFORMATION_STYPE, 48 | characteristics: [{ 49 | cType: types.NAME_CTYPE, 50 | onUpdate: null, 51 | perms: ["pr"], 52 | format: "string", 53 | initialValue: this.name, 54 | supportEvents: false, 55 | supportBonjour: false, 56 | manfDescription: "Name of the accessory", 57 | designedMaxLength: 255 58 | },{ 59 | cType: types.MANUFACTURER_CTYPE, 60 | onUpdate: null, 61 | perms: ["pr"], 62 | format: "string", 63 | initialValue: "Tesla", 64 | supportEvents: false, 65 | supportBonjour: false, 66 | manfDescription: "Manufacturer", 67 | designedMaxLength: 255 68 | },{ 69 | cType: types.MODEL_CTYPE, 70 | onUpdate: null, 71 | perms: ["pr"], 72 | format: "string", 73 | initialValue: "Rev-1", 74 | supportEvents: false, 75 | supportBonjour: false, 76 | manfDescription: "Model", 77 | designedMaxLength: 255 78 | },{ 79 | cType: types.SERIAL_NUMBER_CTYPE, 80 | onUpdate: null, 81 | perms: ["pr"], 82 | format: "string", 83 | initialValue: "A1S2NA45345SF88EW", 84 | supportEvents: false, 85 | supportBonjour: false, 86 | manfDescription: "SN", 87 | designedMaxLength: 255 88 | },{ 89 | cType: types.IDENTIFY_CTYPE, 90 | onUpdate: null, 91 | perms: ["pw"], 92 | format: "bool", 93 | initialValue: false, 94 | supportEvents: false, 95 | supportBonjour: false, 96 | manfDescription: "Identify Accessory", 97 | designedMaxLength: 1 98 | }] 99 | },{ 100 | sType: types.LOCK_MECHANISM_STYPE, 101 | characteristics: [{ 102 | cType: types.NAME_CTYPE, 103 | onUpdate: null, 104 | perms: ["pr"], 105 | format: "string", 106 | initialValue: this.name, 107 | supportEvents: false, 108 | supportBonjour: false, 109 | manfDescription: "Name of service", 110 | designedMaxLength: 255 111 | },{ 112 | cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, 113 | onUpdate: function(value) { that.setPowerState(value); }, 114 | perms: ["pr","ev"], 115 | format: "int", 116 | initialValue: 0, 117 | supportEvents: false, 118 | supportBonjour: false, 119 | manfDescription: "BlaBla", 120 | designedMinValue: 0, 121 | designedMaxValue: 3, 122 | designedMinStep: 1, 123 | designedMaxLength: 1 124 | },{ 125 | cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE, 126 | onUpdate: function(value) { 127 | console.log(that.name + "...changing status...."); 128 | that.setPowerState(value); 129 | }, 130 | perms: ["pr","pw","ev"], 131 | format: "int", 132 | initialValue: 0, 133 | supportEvents: false, 134 | supportBonjour: false, 135 | manfDescription: "BlaBla", 136 | designedMinValue: 0, 137 | designedMaxValue: 1, 138 | designedMinStep: 1, 139 | designedMaxLength: 1 140 | }] 141 | }]; 142 | } 143 | }; 144 | 145 | module.exports.accessory = TeslaAccessory; 146 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/TeslaChargePort.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | /* 18 | var ROOF_CLOSE = 0; 19 | var ROOF_VENT = 1; 20 | var ROOF_COMFORT = 2; 21 | var ROOF_OPEN = 3; 22 | */ 23 | 24 | if (powerOn) { 25 | // tesla.open_charge_port({id:vehicle, lock: 1}, function(response) { 26 | // if (response.result) 27 | // that.log("Starting to Open Charge Port."); 28 | // else 29 | // that.log("Error Opening Charge Port: " + response.reason); 30 | // }); 31 | } 32 | else { 33 | tesla.open_charge_port({id:vehicle, lock: 0}, function(response) { 34 | if (response.result) 35 | that.log("Unlock to Open Charge Port."); 36 | else 37 | that.log("Error Opening Charge Port: " + response.reason); 38 | }); 39 | } 40 | }) 41 | }, 42 | 43 | getServices: function() { 44 | var that = this; 45 | return [{ 46 | sType: types.ACCESSORY_INFORMATION_STYPE, 47 | characteristics: [{ 48 | cType: types.NAME_CTYPE, 49 | onUpdate: null, 50 | perms: ["pr"], 51 | format: "string", 52 | initialValue: this.name, 53 | supportEvents: false, 54 | supportBonjour: false, 55 | manfDescription: "Name of the accessory", 56 | designedMaxLength: 255 57 | },{ 58 | cType: types.MANUFACTURER_CTYPE, 59 | onUpdate: null, 60 | perms: ["pr"], 61 | format: "string", 62 | initialValue: "Tesla", 63 | supportEvents: false, 64 | supportBonjour: false, 65 | manfDescription: "Manufacturer", 66 | designedMaxLength: 255 67 | },{ 68 | cType: types.MODEL_CTYPE, 69 | onUpdate: null, 70 | perms: ["pr"], 71 | format: "string", 72 | initialValue: "Rev-1", 73 | supportEvents: false, 74 | supportBonjour: false, 75 | manfDescription: "Model", 76 | designedMaxLength: 255 77 | },{ 78 | cType: types.SERIAL_NUMBER_CTYPE, 79 | onUpdate: null, 80 | perms: ["pr"], 81 | format: "string", 82 | initialValue: "A1S2NA45345SF88EW", 83 | supportEvents: false, 84 | supportBonjour: false, 85 | manfDescription: "SN", 86 | designedMaxLength: 255 87 | },{ 88 | cType: types.IDENTIFY_CTYPE, 89 | onUpdate: null, 90 | perms: ["pw"], 91 | format: "bool", 92 | initialValue: false, 93 | supportEvents: false, 94 | supportBonjour: false, 95 | manfDescription: "Identify Accessory", 96 | designedMaxLength: 1 97 | }] 98 | },{ 99 | sType: types.LOCK_MECHANISM_STYPE, 100 | characteristics: [{ 101 | cType: types.NAME_CTYPE, 102 | onUpdate: null, 103 | perms: ["pr"], 104 | format: "string", 105 | initialValue: this.name, 106 | supportEvents: false, 107 | supportBonjour: false, 108 | manfDescription: "Name of service", 109 | designedMaxLength: 255 110 | },{ 111 | cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, 112 | onUpdate: function(value) { that.setPowerState(value); }, 113 | perms: ["pr","ev"], 114 | format: "int", 115 | initialValue: 0, 116 | supportEvents: false, 117 | supportBonjour: false, 118 | manfDescription: "BlaBla", 119 | designedMinValue: 0, 120 | designedMaxValue: 3, 121 | designedMinStep: 1, 122 | designedMaxLength: 1 123 | },{ 124 | cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE, 125 | onUpdate: function(value) { 126 | console.log(that.name + "...changing status...."); 127 | that.setPowerState(value); 128 | }, 129 | perms: ["pr","pw","ev"], 130 | format: "int", 131 | initialValue: 0, 132 | supportEvents: false, 133 | supportBonjour: false, 134 | manfDescription: "BlaBla", 135 | designedMinValue: 0, 136 | designedMaxValue: 1, 137 | designedMinStep: 1, 138 | designedMaxLength: 1 139 | }] 140 | }]; 141 | } 142 | }; 143 | 144 | module.exports.accessory = TeslaAccessory; 145 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/TeslaGarage.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var tesla = require("teslams"); 3 | 4 | function TeslaAccessory(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.username = config["username"]; 8 | this.password = config["password"]; 9 | } 10 | 11 | TeslaAccessory.prototype = { 12 | 13 | setPowerState: function(powerOn) { 14 | var that = this; 15 | 16 | tesla.get_vid({email: this.username, password: this.password}, function(vehicle) { 17 | /* 18 | var ROOF_CLOSE = 0; 19 | var ROOF_VENT = 1; 20 | var ROOF_COMFORT = 2; 21 | var ROOF_OPEN = 3; 22 | */ 23 | 24 | if (powerOn) { 25 | tesla.trigger_homelink({id:vehicle, latitude: '40.282517', longitude:'-74.029605'}, function(response) { 26 | if (response.result) 27 | that.log("Started homelink request."); 28 | else 29 | that.log("Error starting homelink request: " + response.reason); 30 | }); 31 | } 32 | else { 33 | tesla.trigger_homelink({id:vehicle, latitude: '40.282517', longitude:'-74.029605'}, function(response) { 34 | if (response.result) 35 | that.log("Stopped homelink request."); 36 | else 37 | that.log("Error stopping homelink request: " + response.reason); 38 | }); 39 | } 40 | }) 41 | }, 42 | 43 | getServices: function() { 44 | var that = this; 45 | return [{ 46 | sType: types.ACCESSORY_INFORMATION_STYPE, 47 | characteristics: [{ 48 | cType: types.NAME_CTYPE, 49 | onUpdate: null, 50 | perms: ["pr"], 51 | format: "string", 52 | initialValue: this.name, 53 | supportEvents: false, 54 | supportBonjour: false, 55 | manfDescription: "Name of the accessory", 56 | designedMaxLength: 255 57 | },{ 58 | cType: types.MANUFACTURER_CTYPE, 59 | onUpdate: null, 60 | perms: ["pr"], 61 | format: "string", 62 | initialValue: "Tesla", 63 | supportEvents: false, 64 | supportBonjour: false, 65 | manfDescription: "Manufacturer", 66 | designedMaxLength: 255 67 | },{ 68 | cType: types.MODEL_CTYPE, 69 | onUpdate: null, 70 | perms: ["pr"], 71 | format: "string", 72 | initialValue: "Rev-1", 73 | supportEvents: false, 74 | supportBonjour: false, 75 | manfDescription: "Model", 76 | designedMaxLength: 255 77 | },{ 78 | cType: types.SERIAL_NUMBER_CTYPE, 79 | onUpdate: null, 80 | perms: ["pr"], 81 | format: "string", 82 | initialValue: "A1S2NA45345SF88EW", 83 | supportEvents: false, 84 | supportBonjour: false, 85 | manfDescription: "SN", 86 | designedMaxLength: 255 87 | },{ 88 | cType: types.IDENTIFY_CTYPE, 89 | onUpdate: null, 90 | perms: ["pw"], 91 | format: "bool", 92 | initialValue: false, 93 | supportEvents: false, 94 | supportBonjour: false, 95 | manfDescription: "Identify Accessory", 96 | designedMaxLength: 1 97 | }] 98 | },{ 99 | sType: types.GARAGE_DOOR_OPENER_STYPE, 100 | characteristics: [{ 101 | cType: types.NAME_CTYPE, 102 | onUpdate: null, 103 | perms: ["pr"], 104 | format: "string", 105 | initialValue: this.name, 106 | supportEvents: false, 107 | supportBonjour: false, 108 | manfDescription: "Name of service", 109 | designedMaxLength: 255 110 | },{ 111 | cType: types.CURRENT_DOOR_STATE_CTYPE, 112 | onUpdate: function(value) { that.setPowerState(value); }, 113 | perms: ["pr","ev"], 114 | format: "int", 115 | initialValue: 0, 116 | supportEvents: false, 117 | supportBonjour: false, 118 | manfDescription: "BlaBla", 119 | designedMinValue: 0, 120 | designedMaxValue: 4, 121 | designedMinStep: 1, 122 | designedMaxLength: 1 123 | },{ 124 | cType: types.TARGET_DOORSTATE_CTYPE, 125 | onUpdate: function(value) { that.setPowerState(value); }, 126 | perms: ["pw","pr","ev"], 127 | format: "int", 128 | initialValue: 0, 129 | supportEvents: false, 130 | supportBonjour: false, 131 | manfDescription: "Change the door state", 132 | designedMinValue: 0, 133 | designedMaxValue: 1, 134 | designedMinStep: 1, 135 | designedMaxLength: 1 136 | }, { 137 | cType: types.OBSTRUCTION_DETECTED_CTYPE, 138 | onUpdate: function(value) { 139 | console.log("Garage Door Obstruction Change:",value); 140 | }, 141 | onRead: function(callback) { 142 | console.log("Garage Door Obstruction Read:"); 143 | }, 144 | perms: ["pr","ev"], 145 | format: "bool", 146 | initialValue: false, 147 | supportEvents: false, 148 | supportBonjour: false, 149 | manfDescription: "BlaBla", 150 | }] 151 | }]; 152 | } 153 | }; 154 | 155 | module.exports.accessory = TeslaAccessory; 156 | -------------------------------------------------------------------------------- /lib/api.js: -------------------------------------------------------------------------------- 1 | var inherits = require('util').inherits; 2 | var EventEmitter = require('events').EventEmitter; 3 | var hap = require("hap-nodejs"); 4 | var hapLegacyTypes = require("hap-nodejs/accessories/types.js"); 5 | var log = require("./logger")._system; 6 | var User = require("./user").User; 7 | var PlatformAccessory = require("./platformAccessory").PlatformAccessory; 8 | 9 | // The official homebridge API is the object we feed the plugin's exported initializer function. 10 | 11 | module.exports = { 12 | API: API 13 | } 14 | 15 | function API() { 16 | this._accessories = {}; // this._accessories[pluginName.accessoryName] = accessory constructor 17 | this._platforms = {}; // this._platforms[pluginName.platformName] = platform constructor 18 | 19 | this._configurableAccessories = {}; 20 | this._dynamicPlatforms = {}; // this._dynamicPlatforms[pluginName.platformName] = platform constructor 21 | 22 | // expose the homebridge API version 23 | this.version = 2.0; 24 | 25 | // expose the User class methods to plugins to get paths. Example: homebridge.user.storagePath() 26 | this.user = User; 27 | 28 | // expose HAP-NodeJS in its entirely for plugins to use instead of making Plugins 29 | // require() it as a dependency - it's a heavy dependency so we don't want it in 30 | // every single plugin. 31 | this.hap = hap; 32 | 33 | // we also need to "bolt on" the legacy "types" constants for older accessories/platforms 34 | // still using the "object literal" style JSON. 35 | this.hapLegacyTypes = hapLegacyTypes; 36 | 37 | this.platformAccessory = PlatformAccessory; 38 | } 39 | 40 | inherits(API, EventEmitter); 41 | 42 | API.prototype.accessory = function(name) { 43 | 44 | // if you passed the "short form" name like "Lockitron" instead of "homebridge-lockitron.Lockitron", 45 | // see if it matches exactly one accessory. 46 | if (name.indexOf('.') == -1) { 47 | var found = []; 48 | for (var fullName in this._accessories) { 49 | if (fullName.split(".")[1] == name) 50 | found.push(fullName); 51 | } 52 | 53 | if (found.length == 1) { 54 | return this._accessories[found[0]]; 55 | } 56 | else if (found.length > 1) { 57 | throw new Error("The requested accessory '" + name + "' has been registered multiple times. Please be more specific by writing one of: " + found.join(", ")); 58 | } 59 | else { 60 | throw new Error("The requested accessory '" + name + "' was not registered by any plugin."); 61 | } 62 | } 63 | else { 64 | 65 | if (!this._accessories[name]) 66 | throw new Error("The requested accessory '" + name + "' was not registered by any plugin."); 67 | 68 | return this._accessories[name]; 69 | } 70 | } 71 | 72 | API.prototype.registerAccessory = function(pluginName, accessoryName, constructor, configurationRequestHandler) { 73 | var fullName = pluginName + "." + accessoryName; 74 | 75 | if (this._accessories[fullName]) 76 | throw new Error("Attempting to register an accessory '" + fullName + "' which has already been registered!"); 77 | 78 | log.info("Registering accessory '%s'", fullName); 79 | 80 | this._accessories[fullName] = constructor; 81 | 82 | // The plugin supports configuration 83 | if (configurationRequestHandler) { 84 | this._configurableAccessories[fullName] = configurationRequestHandler; 85 | } 86 | } 87 | 88 | API.prototype.platform = function(name) { 89 | 90 | // if you passed the "short form" name like "Lockitron" instead of "homebridge-lockitron.Lockitron", 91 | // see if it matches exactly one platform. 92 | if (name.indexOf('.') == -1) { 93 | var found = []; 94 | for (var fullName in this._platforms) { 95 | if (fullName.split(".")[1] == name) 96 | found.push(fullName); 97 | } 98 | 99 | if (found.length == 1) { 100 | return this._platforms[found[0]]; 101 | } 102 | else if (found.length > 1) { 103 | throw new Error("The requested platform '" + name + "' has been registered multiple times. Please be more specific by writing one of: " + found.join(", ")); 104 | } 105 | else { 106 | throw new Error("The requested platform '" + name + "' was not registered by any plugin."); 107 | } 108 | } 109 | else { 110 | 111 | if (!this._platforms[name]) 112 | throw new Error("The requested platform '" + name + "' was not registered by any plugin."); 113 | 114 | return this._platforms[name]; 115 | } 116 | } 117 | 118 | API.prototype.registerPlatform = function(pluginName, platformName, constructor, dynamic) { 119 | var fullName = pluginName + "." + platformName; 120 | 121 | if (this._platforms[fullName]) 122 | throw new Error("Attempting to register a platform '" + fullName + "' which has already been registered!"); 123 | 124 | log.info("Registering platform '%s'", fullName); 125 | 126 | this._platforms[fullName] = constructor; 127 | 128 | if (dynamic) { 129 | this._dynamicPlatforms[fullName] = constructor; 130 | } 131 | } 132 | 133 | API.prototype.registerPlatformAccessories = function(pluginName, platformName, accessories) { 134 | for (var index in accessories) { 135 | var accessory = accessories[index]; 136 | if (!(accessory instanceof PlatformAccessory)) { 137 | throw new Error(pluginName + " - " + platformName + " attempt to register an accessory that isn\'t PlatformAccessory!"); 138 | } 139 | accessory._associatedPlugin = pluginName; 140 | accessory._associatedPlatform = platformName; 141 | } 142 | 143 | this.emit('registerPlatformAccessories', accessories); 144 | } 145 | 146 | API.prototype.updatePlatformAccessories = function(accessories) { 147 | this.emit('updatePlatformAccessories', accessories); 148 | } 149 | 150 | API.prototype.unregisterPlatformAccessories = function(pluginName, platformName, accessories) { 151 | for (var index in accessories) { 152 | var accessory = accessories[index]; 153 | if (!(accessory instanceof PlatformAccessory)) { 154 | throw new Error(pluginName + " - " + platformName + " attempt to unregister an accessory that isn\'t PlatformAccessory!"); 155 | } 156 | } 157 | this.emit('unregisterPlatformAccessories', accessories); 158 | } -------------------------------------------------------------------------------- /lib/plugin.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | var semver = require('semver'); 4 | var User = require('./user').User; 5 | var version = require('./version'); 6 | var log = require("./logger")._system; 7 | 8 | 'use strict'; 9 | 10 | module.exports = { 11 | Plugin: Plugin 12 | } 13 | 14 | /** 15 | * Homebridge Plugin. 16 | * 17 | * Allows for discovering and loading installed Homebridge plugins. 18 | */ 19 | 20 | function Plugin(pluginPath) { 21 | this.pluginPath = pluginPath; // like "/usr/local/lib/node_modules/homebridge-lockitron" 22 | this.initializer; // exported function from the plugin that initializes it 23 | } 24 | 25 | Plugin.prototype.name = function() { 26 | return path.basename(this.pluginPath); 27 | } 28 | 29 | Plugin.prototype.load = function(options) { 30 | options = options || {}; 31 | 32 | // does this plugin exist at all? 33 | if (!fs.existsSync(this.pluginPath)) { 34 | throw new Error("Plugin " + this.pluginPath + " was not found. Make sure the module '" + this.pluginPath + "' is installed."); 35 | } 36 | 37 | // attempt to load package.json 38 | var pjson = Plugin.loadPackageJSON(this.pluginPath); 39 | 40 | // very temporary fix for first wave of plugins 41 | if (pjson.peerDepdendencies && (!pjson.engines || !pjson.engines.homebridge)) { 42 | var engines = pjson.engines || {} 43 | engines.homebridge = pjson.peerDepdendencies.homebridge; 44 | pjson.engines = engines; 45 | } 46 | 47 | // pluck out the HomeBridge version requirement 48 | if (!pjson.engines || !pjson.engines.homebridge) { 49 | throw new Error("Plugin " + this.pluginPath + " does not contain the 'homebridge' package in 'engines'."); 50 | } 51 | 52 | var versionRequired = pjson.engines.homebridge; 53 | 54 | // make sure the version is satisfied by the currently running version of HomeBridge 55 | if (!semver.satisfies(version, versionRequired)) { 56 | throw new Error("Plugin " + this.pluginPath + " requires a HomeBridge version of " + versionRequired + " which does not satisfy the current HomeBridge version of " + version + ". You may need to upgrade your installation of HomeBridge."); 57 | } 58 | 59 | // figure out the main module - index.js unless otherwise specified 60 | var main = pjson.main || "./index.js"; 61 | 62 | var mainPath = path.join(this.pluginPath, main); 63 | 64 | // try to require() it and grab the exported initialization hook 65 | this.initializer = require(mainPath); 66 | } 67 | 68 | Plugin.loadPackageJSON = function(pluginPath) { 69 | // check for a package.json 70 | var pjsonPath = path.join(pluginPath, "package.json"); 71 | var pjson = null; 72 | 73 | if (!fs.existsSync(pjsonPath)) { 74 | throw new Error("Plugin " + pluginPath + " does not contain a package.json."); 75 | } 76 | 77 | try { 78 | // attempt to parse package.json 79 | pjson = JSON.parse(fs.readFileSync(pjsonPath)); 80 | } 81 | catch (err) { 82 | throw new Error("Plugin " + pluginPath + " contains an invalid package.json. Error: " + err); 83 | } 84 | 85 | // make sure the name is prefixed with 'homebridge-' 86 | if (!pjson.name || pjson.name.indexOf('homebridge-') != 0) { 87 | throw new Error("Plugin " + pluginPath + " does not have a package name that begins with 'homebridge-'."); 88 | } 89 | 90 | // verify that it's tagged with the correct keyword 91 | if (!pjson.keywords || pjson.keywords.indexOf("homebridge-plugin") == -1) { 92 | throw new Error("Plugin " + pluginPath + " package.json does not contain the keyword 'homebridge-plugin'."); 93 | } 94 | 95 | return pjson; 96 | } 97 | 98 | Plugin.getDefaultPaths = function() { 99 | var win32 = process.platform === 'win32'; 100 | var paths = []; 101 | 102 | // add the paths used by require() 103 | paths = paths.concat(require.main.paths); 104 | 105 | // THIS SECTION FROM: https://github.com/yeoman/environment/blob/master/lib/resolver.js 106 | 107 | // Adding global npm directories 108 | // We tried using npm to get the global modules path, but it haven't work out 109 | // because of bugs in the parseable implementation of `ls` command and mostly 110 | // performance issues. So, we go with our best bet for now. 111 | if (process.env.NODE_PATH) { 112 | paths = process.env.NODE_PATH.split(path.delimiter) 113 | .filter(function(p) { return !!p; }) // trim out empty values 114 | .concat(paths); 115 | } else { 116 | // Default paths for each system 117 | if (win32) { 118 | paths.push(path.join(process.env.APPDATA, 'npm/node_modules')); 119 | } else { 120 | paths.push('/usr/local/lib/node_modules'); 121 | paths.push('/usr/lib/node_modules'); 122 | } 123 | } 124 | 125 | return paths; 126 | } 127 | 128 | // All search paths we will use to discover installed plugins 129 | Plugin.paths = Plugin.getDefaultPaths(); 130 | 131 | Plugin.addPluginPath = function(pluginPath) { 132 | Plugin.paths.unshift(path.resolve(process.cwd(), pluginPath)); 133 | } 134 | 135 | // Gets all plugins installed on the local system 136 | Plugin.installed = function() { 137 | 138 | var plugins = []; 139 | var pluginsByName = {}; // don't add duplicate plugins 140 | var searchedPaths = {}; // don't search the same paths twice 141 | 142 | // search for plugins among all known paths, in order 143 | for (var index in Plugin.paths) { 144 | var requirePath = Plugin.paths[index]; 145 | 146 | // did we already search this path? 147 | if (searchedPaths[requirePath]) 148 | continue; 149 | 150 | searchedPaths[requirePath] = true; 151 | 152 | // just because this path is in require.main.paths doesn't mean it necessarily exists! 153 | if (!fs.existsSync(requirePath)) 154 | continue; 155 | 156 | var names = fs.readdirSync(requirePath); 157 | 158 | // does this path point inside a single plugin and not a directory containing plugins? 159 | if (fs.existsSync(path.join(requirePath, "package.json"))) 160 | names = [""]; 161 | 162 | // read through each directory in this node_modules folder 163 | for (var index2 in names) { 164 | var name = names[index2]; 165 | 166 | // reconstruct full path 167 | var pluginPath = path.join(requirePath, name); 168 | 169 | // we only care about directories 170 | if (!fs.statSync(pluginPath).isDirectory()) continue; 171 | 172 | // does this module contain a package.json? 173 | var pjson; 174 | try { 175 | // throws an Error if this isn't a homebridge plugin 176 | pjson = Plugin.loadPackageJSON(pluginPath); 177 | } 178 | catch (err) { 179 | // is this "trying" to be a homebridge plugin? if so let you know what went wrong. 180 | if (!name || name.indexOf('homebridge-') == 0) { 181 | log.warn(err.message); 182 | } 183 | 184 | // skip this module 185 | continue; 186 | } 187 | 188 | // get actual name if this path points inside a single plugin 189 | if (!name) name = pjson.name; 190 | 191 | // add it to the return list 192 | if (!pluginsByName[name]) { 193 | pluginsByName[name] = pluginPath; 194 | plugins.push(new Plugin(pluginPath)); 195 | } 196 | else { 197 | log.warn("Warning: skipping plugin found at '" + pluginPath + "' since we already loaded the same plugin from '" + pluginsByName[name] + "'."); 198 | } 199 | } 200 | } 201 | 202 | return plugins; 203 | } 204 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/AD2USB.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var AD2USB = require('ad2usb'); 3 | var CUSTOM_PANEL_LCD_TEXT_CTYPE = "A3E7B8F9-216E-42C1-A21C-97D4E3BE52C8"; 4 | 5 | function AD2USBAccessory(log, config) { 6 | 7 | this.log = log; 8 | this.name = config["name"]; 9 | this.host = config["host"]; 10 | this.port = config["port"]; 11 | this.pin = config["pin"]; 12 | var that = this; 13 | this.currentArmState = 2; 14 | this.currentStateCharacteristic = undefined; 15 | this.targetStateCharacteristic = undefined; 16 | this.lcdCharacteristic = undefined; 17 | 18 | var alarm = AD2USB.connect(this.host, this.port, function() { 19 | 20 | // Send an initial empty character to get status 21 | alarm.send(''); 22 | 23 | // Armed Away 24 | alarm.on('armedAway', function() { 25 | 26 | that.log("Armed to AWAY"); 27 | if (that.currentStateCharacteristic) { 28 | that.currentStateCharacteristic.updateValue(0, null); 29 | } 30 | if (that.targetStateCharacteristic) { 31 | that.targetStateCharacteristic.updateValue(1, null); 32 | } 33 | 34 | }); 35 | 36 | // Armed Stay 37 | alarm.on('armedStay', function() { 38 | 39 | that.log("Armed to STAY"); 40 | if (that.currentStateCharacteristic) { 41 | that.currentStateCharacteristic.updateValue(0, null); 42 | } 43 | if (that.targetStateCharacteristic) { 44 | that.targetStateCharacteristic.updateValue(0, null); 45 | } 46 | 47 | }); 48 | 49 | // Armed Night 50 | alarm.on('armedNight', function() { 51 | 52 | that.log("Armed to NIGHT"); 53 | if (that.currentStateCharacteristic) { 54 | that.currentStateCharacteristic.updateValue(0, null); 55 | } 56 | if (that.targetStateCharacteristic) { 57 | that.targetStateCharacteristic.updateValue(2, null); 58 | } 59 | 60 | }); 61 | 62 | // Disarmed 63 | alarm.on('disarmed', function() { 64 | 65 | that.log("Disarmed"); 66 | if (that.currentStateCharacteristic) { 67 | that.currentStateCharacteristic.updateValue(1, null); 68 | } 69 | if (that.targetStateCharacteristic) { 70 | that.targetStateCharacteristic.updateValue(3, null); 71 | } 72 | 73 | }); 74 | 75 | // Text Change 76 | alarm.on('lcdtext', function(newText) { 77 | 78 | that.log("LCD: " + newText); 79 | if (that.lcdCharacteristic) { 80 | that.lcdCharacteristic.updateValue(newText, null); 81 | } 82 | 83 | }); 84 | 85 | 86 | }); 87 | this.alarm = alarm; 88 | 89 | } 90 | 91 | AD2USBAccessory.prototype = { 92 | 93 | setArmState: function(targetArmState) { 94 | 95 | var that = this; 96 | that.log("Desired target arm state: " + targetArmState); 97 | 98 | // TARGET 99 | // 0 - Stay 100 | // 1 - Away 101 | // 2 - Night 102 | // 3 - Disarm 103 | if (targetArmState == 0) { 104 | that.alarm.armStay(that.pin); 105 | } 106 | else if (targetArmState == 1) { 107 | that.alarm.armAway(that.pin); 108 | } 109 | else if (targetArmState == 2) { 110 | that.alarm.armNight(that.pin); 111 | } 112 | else if (targetArmState == 3) { 113 | that.alarm.disarm(that.pin); 114 | } 115 | 116 | 117 | // CURRENT 118 | // 0 - Armed 119 | // 1 - Disarmed 120 | // 2 - Hold 121 | 122 | }, 123 | 124 | getServices: function() { 125 | var that = this; 126 | return [{ 127 | sType: types.ACCESSORY_INFORMATION_STYPE, 128 | characteristics: [{ 129 | cType: types.NAME_CTYPE, 130 | onUpdate: null, 131 | perms: ["pr"], 132 | format: "string", 133 | initialValue: this.name, 134 | supportEvents: false, 135 | supportBonjour: false, 136 | manfDescription: "Name of the accessory", 137 | designedMaxLength: 255 138 | },{ 139 | cType: types.MANUFACTURER_CTYPE, 140 | onUpdate: null, 141 | perms: ["pr"], 142 | format: "string", 143 | initialValue: "Nutech", 144 | supportEvents: false, 145 | supportBonjour: false, 146 | manfDescription: "Manufacturer", 147 | designedMaxLength: 255 148 | },{ 149 | cType: types.MODEL_CTYPE, 150 | onUpdate: null, 151 | perms: ["pr"], 152 | format: "string", 153 | initialValue: "AD2USB", 154 | supportEvents: false, 155 | supportBonjour: false, 156 | manfDescription: "Model", 157 | designedMaxLength: 255 158 | },{ 159 | cType: types.SERIAL_NUMBER_CTYPE, 160 | onUpdate: null, 161 | perms: ["pr"], 162 | format: "string", 163 | initialValue: "AD2USBIF", 164 | supportEvents: false, 165 | supportBonjour: false, 166 | manfDescription: "SN", 167 | designedMaxLength: 255 168 | },{ 169 | cType: types.IDENTIFY_CTYPE, 170 | onUpdate: null, 171 | perms: ["pw"], 172 | format: "bool", 173 | initialValue: false, 174 | supportEvents: false, 175 | supportBonjour: false, 176 | manfDescription: "Identify Accessory", 177 | designedMaxLength: 1 178 | }] 179 | },{ 180 | sType: types.ALARM_STYPE, 181 | characteristics: [{ 182 | cType: types.NAME_CTYPE, 183 | onUpdate: null, 184 | perms: ["pr"], 185 | format: "string", 186 | initialValue: this.name, 187 | supportEvents: false, 188 | supportBonjour: false, 189 | manfDescription: "Name of service", 190 | designedMaxLength: 255 191 | },{ 192 | cType: types.ALARM_CURRENT_STATE_CTYPE, 193 | onUpdate: null, 194 | onRegister: function(characteristic) { 195 | 196 | that.currentStateCharacteristic = characteristic; 197 | characteristic.eventEnabled = true; 198 | 199 | }, 200 | perms: ["pr","ev"], 201 | format: "int", 202 | initialValue: 2, 203 | supportEvents: true, 204 | supportBonjour: false, 205 | manfDescription: "Alarm current arm state", 206 | designedMaxLength: 1 207 | },{ 208 | cType: types.ALARM_TARGET_STATE_CTYPE, 209 | onUpdate: function(value) { that.setArmState(value); }, 210 | onRegister: function(characteristic) { 211 | 212 | that.targetStateCharacteristic = characteristic; 213 | characteristic.eventEnabled = true; 214 | 215 | }, 216 | perms: ["pw","pr","ev"], 217 | format: "int", 218 | initialValue: 1, 219 | supportEvents: true, 220 | supportBonjour: false, 221 | manfDescription: "Alarm target arm state", 222 | designedMaxLength: 1 223 | }, 224 | { 225 | cType: CUSTOM_PANEL_LCD_TEXT_CTYPE, 226 | onUpdate: null, 227 | onRegister: function(characteristic) { 228 | 229 | that.lcdCharacteristic = characteristic; 230 | characteristic.eventEnabled = true; 231 | 232 | }, 233 | perms: ["pr","ev"], 234 | format: "string", 235 | initialValue: "Unknown", 236 | supportEvents: false, 237 | supportBonjour: false, 238 | manfDescription: "Keypad Text", 239 | designedMaxLength: 64 240 | }] 241 | }]; 242 | } 243 | }; 244 | 245 | module.exports.accessory = AD2USBAccessory; 246 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/platforms/SmartThings.js: -------------------------------------------------------------------------------- 1 | // SmartThings JSON API SmartApp required 2 | // https://github.com/jnewland/SmartThings/blob/master/JSON.groovy 3 | // 4 | var types = require("../api").homebridge.hapLegacyTypes; 5 | var request = require("request"); 6 | 7 | function SmartThingsPlatform(log, config){ 8 | this.log = log; 9 | this.app_id = config["app_id"]; 10 | this.access_token = config["access_token"]; 11 | } 12 | 13 | SmartThingsPlatform.prototype = { 14 | accessories: function(callback) { 15 | this.log("Fetching SmartThings devices..."); 16 | 17 | var that = this; 18 | var foundAccessories = []; 19 | 20 | request.get({ 21 | url: "https://graph.api.smartthings.com/api/smartapps/installations/"+this.app_id+"/devices?access_token="+this.access_token, 22 | json: true 23 | }, function(err, response, json) { 24 | if (!err && response.statusCode == 200) { 25 | if (json['switches'] != undefined) { 26 | json['switches'].map(function(s) { 27 | accessory = new SmartThingsAccessory(that.log, s.name, s.commands); 28 | foundAccessories.push(accessory); 29 | }) 30 | } 31 | if (json['hues'] != undefined) { 32 | json['hues'].map(function(s) { 33 | accessory = new SmartThingsAccessory(that.log, s.name, s.commands); 34 | foundAccessories.push(accessory); 35 | }) 36 | } 37 | callback(foundAccessories); 38 | } else { 39 | that.log("There was a problem authenticating with SmartThings."); 40 | } 41 | }); 42 | 43 | } 44 | } 45 | 46 | function SmartThingsAccessory(log, name, commands) { 47 | // device info 48 | this.name = name; 49 | this.commands = commands; 50 | this.log = log; 51 | } 52 | 53 | SmartThingsAccessory.prototype = { 54 | 55 | command: function(c,value) { 56 | this.log(this.name + " sending command " + c); 57 | var url = this.commands[c]; 58 | if (value != undefined) { 59 | url = this.commands[c] + "&value="+value 60 | } 61 | 62 | var that = this; 63 | request.put({ 64 | url: url 65 | }, function(err, response) { 66 | if (err) { 67 | that.log("There was a problem sending command " + c + " to" + that.name); 68 | that.log(url); 69 | } else { 70 | that.log(that.name + " sent command " + c); 71 | } 72 | }) 73 | }, 74 | 75 | informationCharacteristics: function() { 76 | return [ 77 | { 78 | cType: types.NAME_CTYPE, 79 | onUpdate: null, 80 | perms: ["pr"], 81 | format: "string", 82 | initialValue: this.name, 83 | supportEvents: false, 84 | supportBonjour: false, 85 | manfDescription: "Name of the accessory", 86 | designedMaxLength: 255 87 | },{ 88 | cType: types.MANUFACTURER_CTYPE, 89 | onUpdate: null, 90 | perms: ["pr"], 91 | format: "string", 92 | initialValue: "SmartThings", 93 | supportEvents: false, 94 | supportBonjour: false, 95 | manfDescription: "Manufacturer", 96 | designedMaxLength: 255 97 | },{ 98 | cType: types.MODEL_CTYPE, 99 | onUpdate: null, 100 | perms: ["pr"], 101 | format: "string", 102 | initialValue: "Rev-1", 103 | supportEvents: false, 104 | supportBonjour: false, 105 | manfDescription: "Model", 106 | designedMaxLength: 255 107 | },{ 108 | cType: types.SERIAL_NUMBER_CTYPE, 109 | onUpdate: null, 110 | perms: ["pr"], 111 | format: "string", 112 | initialValue: "A1S2NASF88EW", 113 | supportEvents: false, 114 | supportBonjour: false, 115 | manfDescription: "SN", 116 | designedMaxLength: 255 117 | },{ 118 | cType: types.IDENTIFY_CTYPE, 119 | onUpdate: null, 120 | perms: ["pw"], 121 | format: "bool", 122 | initialValue: false, 123 | supportEvents: false, 124 | supportBonjour: false, 125 | manfDescription: "Identify Accessory", 126 | designedMaxLength: 1 127 | } 128 | ] 129 | }, 130 | 131 | controlCharacteristics: function(that) { 132 | cTypes = [{ 133 | cType: types.NAME_CTYPE, 134 | onUpdate: null, 135 | perms: ["pr"], 136 | format: "string", 137 | initialValue: this.name, 138 | supportEvents: true, 139 | supportBonjour: false, 140 | manfDescription: "Name of service", 141 | designedMaxLength: 255 142 | }] 143 | 144 | if (this.commands['on'] != undefined) { 145 | cTypes.push({ 146 | cType: types.POWER_STATE_CTYPE, 147 | onUpdate: function(value) { 148 | if (value == 0) { 149 | that.command("off") 150 | } else { 151 | that.command("on") 152 | } 153 | }, 154 | perms: ["pw","pr","ev"], 155 | format: "bool", 156 | initialValue: 0, 157 | supportEvents: true, 158 | supportBonjour: false, 159 | manfDescription: "Change the power state", 160 | designedMaxLength: 1 161 | }) 162 | } 163 | 164 | if (this.commands['on'] != undefined) { 165 | cTypes.push({ 166 | cType: types.BRIGHTNESS_CTYPE, 167 | onUpdate: function(value) { that.command("setLevel", value); }, 168 | perms: ["pw","pr","ev"], 169 | format: "int", 170 | initialValue: 0, 171 | supportEvents: true, 172 | supportBonjour: false, 173 | manfDescription: "Adjust Brightness of Light", 174 | designedMinValue: 0, 175 | designedMaxValue: 100, 176 | designedMinStep: 1, 177 | unit: "%" 178 | }) 179 | } 180 | 181 | if (this.commands['setHue'] != undefined) { 182 | cTypes.push({ 183 | cType: types.HUE_CTYPE, 184 | onUpdate: function(value) { that.command("setHue", value); }, 185 | perms: ["pw","pr","ev"], 186 | format: "int", 187 | initialValue: 0, 188 | supportEvents: true, 189 | supportBonjour: false, 190 | manfDescription: "Adjust Hue of Light", 191 | designedMinValue: 0, 192 | designedMaxValue: 360, 193 | designedMinStep: 1, 194 | unit: "arcdegrees" 195 | }) 196 | } 197 | 198 | if (this.commands['setSaturation'] != undefined) { 199 | cTypes.push({ 200 | cType: types.SATURATION_CTYPE, 201 | onUpdate: function(value) { that.command("setSaturation", value); }, 202 | perms: ["pw","pr","ev"], 203 | format: "int", 204 | initialValue: 0, 205 | supportEvents: true, 206 | supportBonjour: false, 207 | manfDescription: "Adjust Brightness of Light", 208 | designedMinValue: 0, 209 | designedMaxValue: 100, 210 | designedMinStep: 1, 211 | unit: "%" 212 | }) 213 | } 214 | 215 | return cTypes 216 | }, 217 | 218 | sType: function() { 219 | if (this.commands['setLevel'] != undefined) { 220 | return types.LIGHTBULB_STYPE 221 | } else { 222 | return types.SWITCH_STYPE 223 | } 224 | }, 225 | 226 | getServices: function() { 227 | var that = this; 228 | var services = [{ 229 | sType: types.ACCESSORY_INFORMATION_STYPE, 230 | characteristics: this.informationCharacteristics(), 231 | }, 232 | { 233 | sType: this.sType(), 234 | characteristics: this.controlCharacteristics(that) 235 | }]; 236 | this.log("Loaded services for " + this.name) 237 | return services; 238 | } 239 | }; 240 | 241 | module.exports.accessory = SmartThingsAccessory; 242 | module.exports.platform = SmartThingsPlatform; 243 | -------------------------------------------------------------------------------- /example-plugins/homebridge-samplePlatform/index.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var Accessory, Service, Characteristic, UUIDGen; 3 | 4 | module.exports = function(homebridge) { 5 | console.log("homebridge API version: " + homebridge.version); 6 | 7 | // Accessory must be created from PlatformAccessory Constructor 8 | Accessory = homebridge.platformAccessory; 9 | 10 | // Service and Characteristic are from hap-nodejs 11 | Service = homebridge.hap.Service; 12 | Characteristic = homebridge.hap.Characteristic; 13 | UUIDGen = homebridge.hap.uuid; 14 | 15 | // For platform plugin to be considered as dynamic platform plugin, 16 | // registerPlatform(pluginName, platformName, constructor, dynamic), dynamic must be true 17 | homebridge.registerPlatform("homebridge-samplePlatform", "SamplePlatform", SamplePlatform, true); 18 | } 19 | 20 | // Platform constructor 21 | // config may be null 22 | // api may be null if launched from old homebridge version 23 | function SamplePlatform(log, config, api) { 24 | console.log("SamplePlatform Init"); 25 | this.log = log; 26 | this.config = config; 27 | this.accessories = []; 28 | 29 | this.requestServer = http.createServer(function(request, response) { 30 | if (request.url === "/add") { 31 | this.addAccessory(); 32 | response.writeHead(204); 33 | response.end(); 34 | } 35 | 36 | if (request.url == "/reachability") { 37 | this.updateAccessoriesReachability(); 38 | response.writeHead(204); 39 | response.end(); 40 | } 41 | 42 | if (request.url == "/remove") { 43 | this.removeAccessory(); 44 | response.writeHead(204); 45 | response.end(); 46 | } 47 | }.bind(this)); 48 | 49 | this.requestServer.listen(18081, function() { 50 | console.log("Server Listening..."); 51 | }); 52 | 53 | if (api) { 54 | // Save the API object as plugin needs to register new accessory via this object. 55 | this.api = api; 56 | 57 | // Listen to event "didFinishLaunching", this means homebridge already finished loading cached accessories 58 | // Platform Plugin should only register new accessory that doesn't exist in homebridge after this event. 59 | // Or start discover new accessories 60 | this.api.on('didFinishLaunching', function() { 61 | console.log("Plugin - DidFinishLaunching"); 62 | }.bind(this)); 63 | } 64 | } 65 | 66 | // Function invoked when homebridge tries to restore cached accessory 67 | // Developer can configure accessory at here (like setup event handler) 68 | // Update current value 69 | SamplePlatform.prototype.configureAccessory = function(accessory) { 70 | console.log("Plugin - Configure Accessory: " + accessory.displayName); 71 | 72 | // set the accessory to reachable if plugin can currently process the accessory 73 | // otherwise set to false and update the reachability later by invoking 74 | // accessory.updateReachability() 75 | accessory.reachable = true; 76 | 77 | accessory.on('identify', function(paired, callback) { 78 | console.log("Identify!!!"); 79 | callback(); 80 | }); 81 | 82 | if (accessory.getService(Service.Lightbulb)) { 83 | accessory.getService(Service.Lightbulb) 84 | .getCharacteristic(Characteristic.On) 85 | .on('set', function(value, callback) { 86 | console.log("Light -> " + value); 87 | callback(); 88 | }); 89 | } 90 | 91 | this.accessories.push(accessory); 92 | } 93 | 94 | //Handler will be invoked when user try to config your plugin 95 | //Callback can be cached and invoke when nessary 96 | SamplePlatform.prototype.configurationRequestHandler = function(context, request, callback) { 97 | console.log("Context: ", JSON.stringify(context)); 98 | console.log("Request: ", JSON.stringify(request)); 99 | 100 | // Check the request response 101 | if (request && request.response && request.response.inputs && request.response.inputs.name) { 102 | this.addAccessory(request.response.inputs.name); 103 | 104 | // Invoke callback with config will let homebridge save the new config into config.json 105 | // Callback = function(response, type, replace, config) 106 | // set "type" to platform if the plugin is trying to modify platforms section 107 | // set "replace" to true will let homebridge replace existing config in config.json 108 | // "config" is the data platform trying to save 109 | callback(null, "platform", true, {"platform":"SamplePlatform", "otherConfig":"SomeData"}); 110 | return; 111 | } 112 | 113 | // - UI Type: Input 114 | // Can be used to request input from user 115 | // User response can be retrieved from request.response.inputs next time 116 | // when configurationRequestHandler being invoked 117 | 118 | var respDict = { 119 | "type": "Interface", 120 | "interface": "input", 121 | "title": "Add Accessory", 122 | "items": [ 123 | { 124 | "id": "name", 125 | "title": "Name", 126 | "placeholder": "Fancy Light" 127 | }//, 128 | // { 129 | // "id": "pw", 130 | // "title": "Password", 131 | // "secure": true 132 | // } 133 | ] 134 | } 135 | 136 | // - UI Type: List 137 | // Can be used to ask user to select something from the list 138 | // User response can be retrieved from request.response.selections next time 139 | // when configurationRequestHandler being invoked 140 | 141 | // var respDict = { 142 | // "type": "Interface", 143 | // "interface": "list", 144 | // "title": "Select Something", 145 | // "allowMultipleSelection": true, 146 | // "items": [ 147 | // "A","B","C" 148 | // ] 149 | // } 150 | 151 | // - UI Type: Instruction 152 | // Can be used to ask user to do something (other than text input) 153 | // Hero image is base64 encoded image data. Not really sure the maximum length HomeKit allows. 154 | 155 | // var respDict = { 156 | // "type": "Interface", 157 | // "interface": "instruction", 158 | // "title": "Almost There", 159 | // "detail": "Please press the button on the bridge to finish the setup.", 160 | // "heroImage": "base64 image data", 161 | // "showActivityIndicator": true, 162 | // "showNextButton": true, 163 | // "buttonText": "Login in browser", 164 | // "actionURL": "https://google.com" 165 | // } 166 | 167 | // Plugin can set context to allow it track setup process 168 | context.ts = "Hello"; 169 | 170 | //invoke callback to update setup UI 171 | callback(respDict); 172 | } 173 | 174 | // Sample function to show how developer can add accessory dynamically from outside event 175 | SamplePlatform.prototype.addAccessory = function(accessoryName) { 176 | console.log("Add Accessory"); 177 | var uuid; 178 | 179 | if (!accessoryName) { 180 | accessoryName = "Test Accessory" 181 | } 182 | 183 | uuid = UUIDGen.generate(accessoryName); 184 | 185 | var newAccessory = new Accessory(accessoryName, uuid); 186 | newAccessory.on('identify', function(paired, callback) { 187 | console.log("Identify!!!"); 188 | callback(); 189 | }); 190 | // Plugin can save context on accessory 191 | // To help restore accessory in configureAccessory() 192 | // newAccessory.context.something = "Something" 193 | 194 | newAccessory.addService(Service.Lightbulb, "Test Light") 195 | .getCharacteristic(Characteristic.On) 196 | .on('set', function(value, callback) { 197 | console.log("Light -> " + value); 198 | callback(); 199 | }); 200 | 201 | this.accessories.push(newAccessory); 202 | this.api.registerPlatformAccessories("homebridge-samplePlatform", "SamplePlatform", [newAccessory]); 203 | } 204 | 205 | SamplePlatform.prototype.updateAccessoriesReachability = function() { 206 | console.log("Update Reachability"); 207 | for (var index in this.accessories) { 208 | var accessory = this.accessories[index]; 209 | accessory.updateReachability(false); 210 | } 211 | } 212 | 213 | // Sample function to show how developer can remove accessory dynamically from outside event 214 | SamplePlatform.prototype.removeAccessory = function() { 215 | console.log("Remove Accessory"); 216 | this.api.unregisterPlatformAccessories("homebridge-samplePlatform", "SamplePlatform", this.accessories); 217 | 218 | this.accessories = []; 219 | } -------------------------------------------------------------------------------- /lib/platformAccessory.js: -------------------------------------------------------------------------------- 1 | var uuid = require("hap-nodejs").uuid; 2 | var Accessory = require("hap-nodejs").Accessory; 3 | var Service = require("hap-nodejs").Service; 4 | var Characteristic = require("hap-nodejs").Characteristic; 5 | var inherits = require('util').inherits; 6 | var EventEmitter = require('events').EventEmitter; 7 | 8 | 'use strict'; 9 | 10 | module.exports = { 11 | PlatformAccessory: PlatformAccessory 12 | } 13 | 14 | function PlatformAccessory(displayName, UUID, category) { 15 | if (!displayName) throw new Error("Accessories must be created with a non-empty displayName."); 16 | if (!UUID) throw new Error("Accessories must be created with a valid UUID."); 17 | if (!uuid.isValid(UUID)) throw new Error("UUID '" + UUID + "' is not a valid UUID. Try using the provided 'generateUUID' function to create a valid UUID from any arbitrary string, like a serial number."); 18 | 19 | this.displayName = displayName; 20 | this.UUID = UUID; 21 | this.category = category || Accessory.Categories.OTHER; 22 | this.services = []; 23 | this.reachable = false; 24 | this.context = {}; 25 | 26 | this._associatedPlugin; 27 | this._associatedPlatform; 28 | this._associatedHAPAccessory; 29 | 30 | this 31 | .addService(Service.AccessoryInformation) 32 | .setCharacteristic(Characteristic.Name, displayName) 33 | .setCharacteristic(Characteristic.Manufacturer, "Default-Manufacturer") 34 | .setCharacteristic(Characteristic.Model, "Default-Model") 35 | .setCharacteristic(Characteristic.SerialNumber, "Default-SerialNumber"); 36 | } 37 | 38 | inherits(PlatformAccessory, EventEmitter); 39 | 40 | PlatformAccessory.prototype.addService = function(service) { 41 | // service might be a constructor like `Service.AccessoryInformation` instead of an instance 42 | // of Service. Coerce if necessary. 43 | if (typeof service === 'function') 44 | service = new (Function.prototype.bind.apply(service, arguments)); 45 | 46 | // check for UUID+subtype conflict 47 | for (var index in this.services) { 48 | var existing = this.services[index]; 49 | if (existing.UUID === service.UUID) { 50 | // OK we have two Services with the same UUID. Check that each defines a `subtype` property and that each is unique. 51 | if (!service.subtype) 52 | throw new Error("Cannot add a Service with the same UUID '" + existing.UUID + "' as another Service in this Accessory without also defining a unique 'subtype' property."); 53 | 54 | if (service.subtype.toString() === existing.subtype.toString()) 55 | throw new Error("Cannot add a Service with the same UUID '" + existing.UUID + "' and subtype '" + existing.subtype + "' as another Service in this Accessory."); 56 | } 57 | } 58 | 59 | this.services.push(service); 60 | 61 | if (this._associatedHAPAccessory) { 62 | this._associatedHAPAccessory.addService(service); 63 | } 64 | return service; 65 | } 66 | 67 | PlatformAccessory.prototype.removeService = function(service) { 68 | var targetServiceIndex; 69 | 70 | for (var index in this.services) { 71 | var existingService = this.services[index]; 72 | 73 | if (existingService === service) { 74 | targetServiceIndex = index; 75 | break; 76 | } 77 | } 78 | 79 | if (targetServiceIndex) { 80 | this.services.splice(targetServiceIndex, 1); 81 | service.removeAllListeners(); 82 | 83 | if (this._associatedHAPAccessory) { 84 | this._associatedHAPAccessory.removeService(service); 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * searchs for a Service in the services collection and returns the first Service object that matches. 91 | * If multiple services of the same type are present in one accessory, use getServiceByUUIDAndSubType instead. 92 | * @param {ServiceConstructor|string} name 93 | * @returns Service 94 | */ 95 | PlatformAccessory.prototype.getService = function(name) { 96 | for (var index in this.services) { 97 | var service = this.services[index]; 98 | 99 | if (typeof name === 'string' && (service.displayName === name || service.name === name)) 100 | return service; 101 | else if (typeof name === 'function' && ((service instanceof name) || (name.UUID === service.UUID))) 102 | return service; 103 | } 104 | } 105 | 106 | /** 107 | * searchs for a Service in the services collection and returns the first Service object that matches. 108 | * If multiple services of the same type are present in one accessory, use getServiceByUUIDAndSubType instead. 109 | * @param {string} UUID Can be an UUID, a service.displayName, or a constructor of a Service 110 | * @param {string} subtype A subtype string to match 111 | * @returns Service 112 | */ 113 | PlatformAccessory.prototype.getServiceByUUIDAndSubType = function(UUID, subtype) { 114 | for (var index in this.services) { 115 | var service = this.services[index]; 116 | 117 | if (typeof UUID === 'string' && (service.displayName === UUID || service.name === UUID) && service.subtype === subtype ) 118 | return service; 119 | else if (typeof UUID === 'function' && ((service instanceof UUID) || (UUID.UUID === service.UUID)) && service.subtype === subtype) 120 | return service; 121 | } 122 | } 123 | 124 | 125 | PlatformAccessory.prototype.updateReachability = function(reachable) { 126 | this.reachable = reachable; 127 | 128 | if (this._associatedHAPAccessory) { 129 | this._associatedHAPAccessory.updateReachability(reachable); 130 | } 131 | } 132 | 133 | PlatformAccessory.prototype._prepareAssociatedHAPAccessory = function () { 134 | this._associatedHAPAccessory = new Accessory(this.displayName, this.UUID); 135 | this._associatedHAPAccessory._sideloadServices(this.services); 136 | this._associatedHAPAccessory.category = this.category; 137 | this._associatedHAPAccessory.reachable = this.reachable; 138 | this._associatedHAPAccessory.on('identify', function(paired, callback) { 139 | if (this.listeners('identify').length > 0) { 140 | // allow implementors to identify this Accessory in whatever way is appropriate, and pass along 141 | // the standard callback for completion. 142 | this.emit('identify', paired, callback); 143 | } else { 144 | callback(); 145 | } 146 | }.bind(this)); 147 | } 148 | 149 | PlatformAccessory.prototype._dictionaryPresentation = function() { 150 | var accessory = {}; 151 | 152 | accessory.plugin = this._associatedPlugin; 153 | accessory.platform = this._associatedPlatform; 154 | accessory.displayName = this.displayName; 155 | accessory.UUID = this.UUID; 156 | accessory.category = this.category; 157 | accessory.context = this.context; 158 | 159 | var services = []; 160 | for (var index in this.services) { 161 | var service = this.services[index]; 162 | var servicePresentation = {}; 163 | servicePresentation.displayName = service.displayName; 164 | servicePresentation.UUID = service.UUID; 165 | servicePresentation.subtype = service.subtype; 166 | 167 | var characteristics = []; 168 | for (var cIndex in service.characteristics) { 169 | var characteristic = service.characteristics[cIndex]; 170 | var characteristicPresentation = {}; 171 | characteristicPresentation.displayName = characteristic.displayName; 172 | characteristicPresentation.UUID = characteristic.UUID; 173 | characteristicPresentation.props = characteristic.props; 174 | characteristicPresentation.value = characteristic.value; 175 | characteristics.push(characteristicPresentation); 176 | } 177 | 178 | servicePresentation.characteristics = characteristics; 179 | services.push(servicePresentation); 180 | } 181 | 182 | accessory.services = services; 183 | return accessory; 184 | } 185 | 186 | PlatformAccessory.prototype._configFromData = function(data) { 187 | this._associatedPlugin = data.plugin; 188 | this._associatedPlatform = data.platform; 189 | this.displayName = data.displayName; 190 | this.UUID = data.UUID; 191 | this.category = data.category; 192 | this.context = data.context; 193 | this.reachable = false; 194 | 195 | var services = []; 196 | for (var index in data.services) { 197 | var service = data.services[index]; 198 | var hapService = new Service(service.displayName, service.UUID, service.subtype); 199 | 200 | var characteristics = []; 201 | for (var cIndex in service.characteristics) { 202 | var characteristic = service.characteristics[cIndex]; 203 | var hapCharacteristic = new Characteristic(characteristic.displayName, characteristic.UUID, characteristic.props); 204 | hapCharacteristic.value = characteristic.value; 205 | characteristics.push(hapCharacteristic); 206 | } 207 | 208 | hapService._sideloadCharacteristics(characteristics); 209 | services.push(hapService); 210 | } 211 | 212 | this.services = services; 213 | } 214 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/accessories/HomeMaticThermo.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var request = require("request"); 3 | 4 | function HomeMaticThermo(log, config) { 5 | this.log = log; 6 | this.name = config["name"]; 7 | this.ccuIDTargetTemp = config["ccu_id_TargetTemp"]; 8 | this.ccuIDCurrentTemp = config["ccu_id_CurrentTemp"]; 9 | this.ccuIDControlMode = config["ccu_id_ControlMode"]; 10 | this.ccuIDManuMode = config["ccu_id_ManuMode"]; 11 | this.ccuIDAutoMode = config["ccu_id_AutoMode"]; 12 | this.ccuIP = config["ccu_ip"]; 13 | } 14 | 15 | HomeMaticThermo.prototype = { 16 | 17 | setTargetTemperature: function(value) { 18 | 19 | var that = this; 20 | 21 | this.log("Setting target Temperature of CCU to " + value); 22 | this.log(this.ccuIDTargetTemp + " " + value); 23 | 24 | request.put({ 25 | url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+this.ccuIDTargetTemp+"&new_value="+ value, 26 | }, function(err, response, body) { 27 | 28 | if (!err && response.statusCode == 200) { 29 | that.log("State change complete."); 30 | } 31 | else { 32 | that.log("Error '"+err+"' setting Temperature: " + body); 33 | } 34 | }); 35 | }, 36 | getCurrentTemperature: function(callback) { 37 | 38 | var that = this; 39 | 40 | this.log("Getting current Temperature of CCU"); 41 | request.get({ 42 | url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDCurrentTemp, 43 | }, function(err, response, body) { 44 | 45 | if (!err && response.statusCode == 200) { 46 | 47 | //that.log("Response:"+response.body); 48 | var responseString = response.body.substring(83,87); 49 | //that.log(responseString); 50 | callback(parseFloat(responseString)); 51 | //that.log("Getting current temperature complete."); 52 | } 53 | else { 54 | that.log("Error '"+err+"' getting Temperature: " + body); 55 | } 56 | }); 57 | }, 58 | getTargetTemperature: function(callback) { 59 | 60 | var that = this; 61 | 62 | this.log("Getting target Temperature of CCU"); 63 | request.get({ 64 | url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDTargetTemp, 65 | }, function(err, response, body) { 66 | 67 | if (!err && response.statusCode == 200) { 68 | 69 | //that.log("Response:"+response.body); 70 | var responseString = response.body.substring(83,87); 71 | //that.log(responseString); 72 | callback(parseFloat(responseString)); 73 | //that.log("Getting target temperature complete."); 74 | } 75 | else { 76 | that.log("Error '"+err+"' getting Temperature: " + body); 77 | } 78 | }); 79 | }, 80 | getMode: function(callback) { 81 | 82 | var that = this; 83 | 84 | //this.log("Getting target Mode of CCU"); 85 | //this.log(this.ccuID+ value); 86 | 87 | request.get({ 88 | url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDControlMode, 89 | }, function(err, response, body) { 90 | 91 | if (!err && response.statusCode == 200) { 92 | 93 | //that.log("Response:"+response.body); 94 | var responseInt = response.body.substring(83,84); 95 | //that.log(responseString); 96 | if (responseInt == 1) 97 | { callback(parseInt("0")); } 98 | if (responseInt == 0) 99 | { callback(parseInt("1")); } 100 | //that.log("Getting mode complete."); 101 | } 102 | else { 103 | that.log("Error '"+err+"' getting Mode: " + body); 104 | } 105 | }); 106 | }, 107 | setMode: function(value) { 108 | 109 | var that = this; 110 | 111 | //this.log("Seting target Mode of CCU:" + value); 112 | var modvalue; 113 | var dpID; 114 | switch(value) { 115 | case 3: {modvalue = "true";dpID=this.ccuIDAutoMode;break;} //auto 116 | case 1: {modvalue = "true";dpID=this.ccuIDAutoMode;break;} //heating => auto 117 | default: {modvalue = "1";dpID=this.ccuIDManuMode;} //default => off (manual) 118 | } 119 | 120 | request.put({ 121 | url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+dpID+"&new_value="+ modvalue, 122 | }, function(err, response, body) { 123 | 124 | if (!err && response.statusCode == 200) { 125 | //that.log("Setting Mode complete."); 126 | } 127 | else { 128 | that.log("Error '"+err+"' setting Mode: " + body); 129 | } 130 | }); 131 | }, 132 | getServices: function() { 133 | var that = this; 134 | return [{ 135 | sType: types.ACCESSORY_INFORMATION_STYPE, 136 | characteristics: [{ 137 | cType: types.NAME_CTYPE, 138 | onUpdate: null, 139 | perms: ["pr"], 140 | format: "string", 141 | initialValue: this.name, 142 | supportEvents: false, 143 | supportBonjour: false, 144 | manfDescription: "Name of the accessory", 145 | designedMaxLength: 255 146 | },{ 147 | cType: types.MANUFACTURER_CTYPE, 148 | onUpdate: null, 149 | perms: ["pr"], 150 | format: "string", 151 | initialValue: "test", 152 | supportEvents: false, 153 | supportBonjour: false, 154 | manfDescription: "Manufacturer", 155 | designedMaxLength: 255 156 | },{ 157 | cType: types.MODEL_CTYPE, 158 | onUpdate: null, 159 | perms: ["pr"], 160 | format: "string", 161 | initialValue: "test", 162 | supportEvents: false, 163 | supportBonjour: false, 164 | manfDescription: "Model", 165 | designedMaxLength: 255 166 | },{ 167 | cType: types.SERIAL_NUMBER_CTYPE, 168 | onUpdate: null, 169 | perms: ["pr"], 170 | format: "string", 171 | initialValue: "A1S2NREF88EW", 172 | supportEvents: false, 173 | supportBonjour: false, 174 | manfDescription: "SN", 175 | designedMaxLength: 255 176 | },{ 177 | cType: types.IDENTIFY_CTYPE, 178 | onUpdate: null, 179 | perms: ["pw"], 180 | format: "bool", 181 | initialValue: false, 182 | supportEvents: false, 183 | supportBonjour: false, 184 | manfDescription: "Identify Accessory", 185 | designedMaxLength: 1 186 | }] 187 | },{ 188 | sType: types.THERMOSTAT_STYPE, 189 | characteristics: [{ 190 | cType: types.NAME_CTYPE, 191 | onUpdate: null, 192 | perms: ["pr"], 193 | format: "string", 194 | initialValue: this.name, 195 | supportEvents: false, 196 | supportBonjour: false, 197 | manfDescription: "Name of service", 198 | designedMaxLength: 255 199 | },{ 200 | cType: types.CURRENTHEATINGCOOLING_CTYPE, 201 | onRead: function(callback) { that.getMode(callback); }, 202 | perms: ["pr","ev"], 203 | format: "int", 204 | initialValue: 0, 205 | supportEvents: false, 206 | supportBonjour: false, 207 | manfDescription: "Current Mode", 208 | designedMaxLength: 1, 209 | designedMinValue: 0, 210 | designedMaxValue: 2, 211 | designedMinStep: 1, 212 | },{ 213 | cType: types.TARGETHEATINGCOOLING_CTYPE, 214 | onRead: function(callback) { that.getMode(callback); }, 215 | onUpdate: function(value) { that.setMode(value);}, 216 | perms: ["pw","pr","ev"], 217 | format: "int", 218 | initialValue: 0, 219 | supportEvents: false, 220 | supportBonjour: false, 221 | manfDescription: "Target Mode", 222 | designedMinValue: 0, 223 | designedMaxValue: 3, 224 | designedMinStep: 1, 225 | },{ 226 | cType: types.CURRENT_TEMPERATURE_CTYPE, 227 | onRead: function(callback) { that.getCurrentTemperature(callback); }, 228 | onUpdate: null, 229 | perms: ["pr","ev"], 230 | format: "float", 231 | initialValue: 13.0, 232 | supportEvents: false, 233 | supportBonjour: false, 234 | manfDescription: "Current Temperature", 235 | unit: "celsius" 236 | },{ 237 | cType: types.TARGET_TEMPERATURE_CTYPE, 238 | onUpdate: function(value) { that.setTargetTemperature(value); }, 239 | onRead: function(callback) { that.getTargetTemperature(callback); }, 240 | perms: ["pw","pr","ev"], 241 | format: "float", 242 | initialValue: 19.0, 243 | supportEvents: false, 244 | supportBonjour: false, 245 | manfDescription: "Target Temperature", 246 | designedMinValue: 4, 247 | designedMaxValue: 25, 248 | designedMinStep: 0.1, 249 | unit: "celsius" 250 | },{ 251 | cType: types.TEMPERATURE_UNITS_CTYPE, 252 | onUpdate: null, 253 | perms: ["pr","ev"], 254 | format: "int", 255 | initialValue: 0, 256 | supportEvents: false, 257 | supportBonjour: false, 258 | manfDescription: "Unit" 259 | }] 260 | }]; 261 | } 262 | }; 263 | 264 | module.exports.accessory = HomeMaticThermo; 265 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/platforms/TelldusLive.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var TellduAPI = require("telldus-live"); 3 | 4 | function TelldusLivePlatform(log, config) { 5 | var that = this; 6 | that.log = log; 7 | 8 | that.isLoggedIn = false; 9 | 10 | // Login to Telldus Live! 11 | that.cloud = new TellduAPI.TelldusAPI({publicKey: config["public_key"], privateKey: config["private_key"]}) 12 | .login(config["token"], config["token_secret"], function(err, user) { 13 | if (!!err) that.log("Login error: " + err.message); 14 | that.log("User logged in: " + user.firstname + " " + user.lastname + ", " + user.email); 15 | that.isLoggedIn = true; 16 | } 17 | ); 18 | } 19 | 20 | TelldusLivePlatform.prototype = { 21 | 22 | accessories: function(callback) { 23 | var that = this; 24 | 25 | that.log("Fetching devices..."); 26 | 27 | that.cloud.getDevices(function(err, devices) { 28 | 29 | if (!!err) return that.log('getDevices: ' + err.message); 30 | 31 | var foundAccessories = []; 32 | 33 | // Clean non device 34 | for (var i = 0; i < devices.length; i++) { 35 | if (devices[i].type != 'device') { 36 | devices.splice(i, 1); 37 | } 38 | } 39 | 40 | for (var i = 0; i < devices.length; i++) { 41 | if (devices[i].type === 'device') { 42 | TelldusLiveAccessory.create(that.log, devices[i], that.cloud, function(err, accessory) { 43 | if (!!err) that.log("Couldn't load device info"); 44 | foundAccessories.push(accessory); 45 | if (foundAccessories.length >= devices.length) { 46 | callback(foundAccessories); 47 | } 48 | }); 49 | } 50 | } 51 | 52 | }); 53 | } 54 | }; 55 | 56 | var TelldusLiveAccessory = function TelldusLiveAccessory(log, cloud, device) { 57 | 58 | this.log = log; 59 | this.cloud = cloud; 60 | 61 | var m = device.model ? device.model.split(':') : ['unknown', 'unknown'] ; 62 | 63 | // Set accessory info 64 | this.device = device; 65 | this.id = device.id; 66 | this.name = device.name; 67 | this.manufacturer = m[1]; 68 | this.model = m[0]; 69 | this.state = device.state; 70 | this.stateValue = device.stateValue; 71 | this.status = device.status; 72 | }; 73 | 74 | TelldusLiveAccessory.create = function (log, device, cloud, callback) { 75 | 76 | cloud.getDeviceInfo(device, function(err, device) { 77 | 78 | if (!!err) that.log("Couldn't load device info"); 79 | 80 | callback(err, new TelldusLiveAccessory(log, cloud, device)); 81 | }); 82 | }; 83 | 84 | TelldusLiveAccessory.prototype = { 85 | 86 | dimmerValue: function() { 87 | 88 | if (this.state === 1) { 89 | return 100; 90 | } 91 | 92 | if (this.state === 16 && this.stateValue != "unde") { 93 | return parseInt(this.stateValue * 100 / 255); 94 | } 95 | 96 | return 0; 97 | }, 98 | 99 | informationCharacteristics: function() { 100 | var that = this; 101 | 102 | informationCharacteristics = [ 103 | { 104 | cType: types.NAME_CTYPE, 105 | onUpdate: null, 106 | perms: ["pr"], 107 | format: "string", 108 | initialValue: that.name, 109 | supportEvents: false, 110 | supportBonjour: false, 111 | manfDescription: "Name of the accessory", 112 | designedMaxLength: 255 113 | },{ 114 | cType: types.MANUFACTURER_CTYPE, 115 | onUpdate: null, 116 | perms: ["pr"], 117 | format: "string", 118 | initialValue: that.manufacturer, 119 | supportEvents: false, 120 | supportBonjour: false, 121 | manfDescription: "Manufacturer", 122 | designedMaxLength: 255 123 | },{ 124 | cType: types.MODEL_CTYPE, 125 | onUpdate: null, 126 | perms: ["pr"], 127 | format: "string", 128 | initialValue: that.model, 129 | supportEvents: false, 130 | supportBonjour: false, 131 | manfDescription: "Model", 132 | designedMaxLength: 255 133 | },{ 134 | cType: types.SERIAL_NUMBER_CTYPE, 135 | onUpdate: null, 136 | perms: ["pr"], 137 | format: "string", 138 | initialValue: "A1S2NASF88EW", 139 | supportEvents: false, 140 | supportBonjour: false, 141 | manfDescription: "SN", 142 | designedMaxLength: 255 143 | },{ 144 | cType: types.IDENTIFY_CTYPE, 145 | onUpdate: function () { 146 | that.cloud.onOffDevice(that.device, true, function(err, result) { 147 | if (!!err) that.log("Error: " + err.message); 148 | that.cloud.onOffDevice(that.device, false, function(err, result) { 149 | if (!!err) that.log("Error: " + err.message); 150 | that.cloud.onOffDevice(that.device, true, function(err, result) { 151 | if (!!err) that.log("Error: " + err.message); 152 | that.cloud.onOffDevice(that.device, false, function(err, result) { 153 | if (!!err) that.log("Error: " + err.message); 154 | that.cloud.onOffDevice(that.device, true, function(err, result) { 155 | if (!!err) that.log("Error: " + err.message); 156 | }) 157 | }) 158 | }) 159 | }) 160 | }) 161 | }, 162 | perms: ["pw"], 163 | format: "bool", 164 | initialValue: false, 165 | supportEvents: false, 166 | supportBonjour: false, 167 | manfDescription: "Identify Accessory", 168 | designedMaxLength: 1 169 | } 170 | ]; 171 | return informationCharacteristics; 172 | }, 173 | 174 | controlCharacteristics: function() { 175 | var that = this; 176 | 177 | cTypes = [{ 178 | cType: types.NAME_CTYPE, 179 | onUpdate: null, 180 | perms: ["pr"], 181 | format: "string", 182 | initialValue: that.name, 183 | supportEvents: true, 184 | supportBonjour: false, 185 | manfDescription: "Name of service", 186 | designedMaxLength: 255 187 | }] 188 | 189 | cTypes.push({ 190 | cType: types.POWER_STATE_CTYPE, 191 | onUpdate: function(value) { 192 | if (value == 1) { 193 | that.cloud.onOffDevice(that.device, value, function(err, result) { 194 | if (!!err) { 195 | that.log("Error: " + err.message) 196 | } else { 197 | that.log(that.name + " - Updated power state: " + (value === true ? 'ON' : 'OFF')); 198 | } 199 | }); 200 | } else { 201 | that.cloud.onOffDevice(that.device, value, function(err, result) { 202 | if (!!err) { 203 | that.log("Error: " + err.message) 204 | } else { 205 | that.log(that.name + " - Updated power state: " + (value === true ? 'ON' : 'OFF')); 206 | } 207 | }); 208 | } 209 | }, 210 | perms: ["pw","pr","ev"], 211 | format: "bool", 212 | initialValue: (that.state != 2 && (that.state === 16 && that.stateValue != "0")) ? 1 : 0, 213 | supportEvents: true, 214 | supportBonjour: false, 215 | manfDescription: "Change the power state", 216 | designedMaxLength: 1 217 | }) 218 | 219 | if (that.model === "selflearning-dimmer") { 220 | cTypes.push({ 221 | cType: types.BRIGHTNESS_CTYPE, 222 | onUpdate: function (value) { 223 | that.cloud.dimDevice(that.device, (255 * (value / 100)), function (err, result) { 224 | if (!!err) { 225 | that.log("Error: " + err.message); 226 | } else { 227 | that.log(that.name + " - Updated brightness: " + value); 228 | } 229 | }); 230 | }, 231 | perms: ["pw", "pr", "ev"], 232 | format: "int", 233 | initialValue: that.dimmerValue(), 234 | supportEvents: true, 235 | supportBonjour: false, 236 | manfDescription: "Adjust Brightness of Light", 237 | designedMinValue: 0, 238 | designedMaxValue: 100, 239 | designedMinStep: 1, 240 | unit: "%" 241 | }) 242 | } 243 | 244 | return cTypes 245 | }, 246 | 247 | getServices: function() { 248 | 249 | var services = [ 250 | { 251 | sType: types.ACCESSORY_INFORMATION_STYPE, 252 | characteristics: this.informationCharacteristics() 253 | }, 254 | { 255 | sType: types.LIGHTBULB_STYPE, 256 | characteristics: this.controlCharacteristics() 257 | } 258 | ]; 259 | 260 | return services; 261 | } 262 | }; 263 | 264 | module.exports.platform = TelldusLivePlatform; 265 | module.exports.accessory = TelldusLiveAccessory; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /homebridge-legacy-plugins/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /homebridge-legacy-plugins/platforms/LIFx.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // LiFX Platform Shim for HomeBridge 4 | // 5 | // Remember to add platform to config.json. Example: 6 | // "platforms": [ 7 | // { 8 | // "platform": "LIFx", // required 9 | // "name": "LIFx", // required 10 | // "access_token": "access token", // required 11 | // "use_lan": "true" // optional set to "true" (gets and sets over the lan) or "get" (gets only over the lan) 12 | // } 13 | // ], 14 | // 15 | // When you attempt to add a device, it will ask for a "PIN code". 16 | // The default code for all HomeBridge accessories is 031-45-154. 17 | // 18 | 19 | var Service = require("../api").homebridge.hap.Service; 20 | var Characteristic = require("../api").homebridge.hap.Characteristic; 21 | var lifxRemoteObj = require('lifx-api'); 22 | var lifx_remote; 23 | 24 | var lifxLanObj; 25 | var lifx_lan; 26 | var use_lan; 27 | 28 | function LIFxPlatform(log, config){ 29 | // auth info 30 | this.access_token = config["access_token"]; 31 | 32 | lifx_remote = new lifxRemoteObj(this.access_token); 33 | 34 | // use remote or lan api ? 35 | use_lan = config["use_lan"] || false; 36 | 37 | if (use_lan != false) { 38 | lifxLanObj = require('lifx'); 39 | lifx_lan = lifxLanObj.init(); 40 | } 41 | 42 | this.log = log; 43 | } 44 | 45 | LIFxPlatform.prototype = { 46 | accessories: function(callback) { 47 | this.log("Fetching LIFx devices."); 48 | 49 | var that = this; 50 | var foundAccessories = []; 51 | 52 | lifx_remote.listLights("all", function(body) { 53 | var bulbs = JSON.parse(body); 54 | 55 | for(var i = 0; i < bulbs.length; i ++) { 56 | var accessory = new LIFxBulbAccessory(that.log, bulbs[i]); 57 | foundAccessories.push(accessory); 58 | } 59 | callback(foundAccessories) 60 | }); 61 | } 62 | } 63 | 64 | function LIFxBulbAccessory(log, bulb) { 65 | // device info 66 | this.name = bulb.label; 67 | this.model = bulb.product_name; 68 | this.deviceId = bulb.id; 69 | this.serial = bulb.uuid; 70 | this.capabilities = bulb.capabilities; 71 | this.log = log; 72 | } 73 | 74 | LIFxBulbAccessory.prototype = { 75 | getLan: function(type, callback){ 76 | var that = this; 77 | 78 | if (!lifx_lan.bulbs[this.deviceId]) { 79 | callback(new Error("Device not found"), false); 80 | return; 81 | } 82 | 83 | lifx_lan.requestStatus(); 84 | lifx_lan.on('bulbstate', function(bulb) { 85 | if (callback == null) { 86 | return; 87 | } 88 | 89 | if (bulb.addr.toString('hex') == that.deviceId) { 90 | switch(type) { 91 | case "power": 92 | callback(null, bulb.state.power > 0); 93 | break; 94 | case "brightness": 95 | callback(null, Math.round(bulb.state.brightness * 100 / 65535)); 96 | break; 97 | case "hue": 98 | callback(null, Math.round(bulb.state.hue * 360 / 65535)); 99 | break; 100 | case "saturation": 101 | callback(null, Math.round(bulb.state.saturation * 100 / 65535)); 102 | break; 103 | } 104 | 105 | callback = null 106 | } 107 | }); 108 | }, 109 | getRemote: function(type, callback){ 110 | var that = this; 111 | 112 | lifx_remote.listLights("id:"+ that.deviceId, function(body) { 113 | var bulb = JSON.parse(body); 114 | 115 | if (bulb.connected != true) { 116 | callback(new Error("Device not found"), false); 117 | return; 118 | } 119 | 120 | switch(type) { 121 | case "power": 122 | callback(null, bulb.power == "on" ? 1 : 0); 123 | break; 124 | case "brightness": 125 | callback(null, Math.round(bulb.brightness * 100)); 126 | break; 127 | case "hue": 128 | callback(null, bulb.color.hue); 129 | break; 130 | case "saturation": 131 | callback(null, Math.round(bulb.color.saturation * 100)); 132 | break; 133 | } 134 | }); 135 | }, 136 | identify: function(callback) { 137 | lifx_remote.breatheEffect("id:"+ this.deviceId, 'green', null, 1, 3, false, true, 0.5, function (body) { 138 | callback(); 139 | }); 140 | }, 141 | setLanColor: function(type, value, callback){ 142 | var bulb = lifx_lan.bulbs[this.deviceId]; 143 | 144 | if (!bulb) { 145 | callback(new Error("Device not found"), false); 146 | return; 147 | } 148 | 149 | var state = { 150 | hue: bulb.state.hue, 151 | saturation: bulb.state.saturation, 152 | brightness: bulb.state.brightness, 153 | kelvin: bulb.state.kelvin 154 | }; 155 | 156 | var scale = type == "hue" ? 360 : 100; 157 | 158 | state[type] = Math.round(value * 65535 / scale) & 0xffff; 159 | lifx_lan.lightsColour(state.hue, state.saturation, state.brightness, state.kelvin, 0, bulb); 160 | 161 | callback(null); 162 | }, 163 | setLanPower: function(state, callback){ 164 | var bulb = lifx_lan.bulbs[this.deviceId]; 165 | 166 | if (!bulb) { 167 | callback(new Error("Device not found"), false); 168 | return; 169 | } 170 | 171 | if (state) { 172 | lifx_lan.lightsOn(bulb); 173 | } 174 | else { 175 | lifx_lan.lightsOff(bulb); 176 | } 177 | 178 | callback(null); 179 | }, 180 | setRemoteColor: function(type, value, callback){ 181 | var color; 182 | 183 | switch(type) { 184 | case "brightness": 185 | color = "brightness:" + (value / 100); 186 | break; 187 | case "hue": 188 | color = "hue:" + value; 189 | break; 190 | case "saturation": 191 | color = "saturation:" + (value / 100); 192 | break; 193 | } 194 | 195 | lifx_remote.setColor("id:"+ this.deviceId, color, 0, null, function (body) { 196 | callback(); 197 | }); 198 | }, 199 | setRemotePower: function(state, callback){ 200 | var that = this; 201 | 202 | lifx_remote.setPower("id:"+ that.deviceId, (state == 1 ? "on" : "off"), 0, function (body) { 203 | callback(); 204 | }); 205 | }, 206 | getServices: function() { 207 | var that = this; 208 | var services = [] 209 | var service = new Service.Lightbulb(this.name); 210 | 211 | switch(use_lan) { 212 | case true: 213 | case "true": 214 | // gets and sets over the lan api 215 | service 216 | .getCharacteristic(Characteristic.On) 217 | .on('get', function(callback) { that.getLan("power", callback);}) 218 | .on('set', function(value, callback) {that.setLanPower(value, callback);}); 219 | 220 | service 221 | .addCharacteristic(Characteristic.Brightness) 222 | .on('get', function(callback) { that.getLan("brightness", callback);}) 223 | .on('set', function(value, callback) { that.setLanColor("brightness", value, callback);}); 224 | 225 | if (this.capabilities.has_color == true) { 226 | service 227 | .addCharacteristic(Characteristic.Hue) 228 | .on('get', function(callback) { that.getLan("hue", callback);}) 229 | .on('set', function(value, callback) { that.setLanColor("hue", value, callback);}); 230 | 231 | service 232 | .addCharacteristic(Characteristic.Saturation) 233 | .on('get', function(callback) { that.getLan("saturation", callback);}) 234 | .on('set', function(value, callback) { that.setLanColor("saturation", value, callback);}); 235 | } 236 | break; 237 | case "get": 238 | // gets over the lan api, sets over the remote api 239 | service 240 | .getCharacteristic(Characteristic.On) 241 | .on('get', function(callback) { that.getLan("power", callback);}) 242 | .on('set', function(value, callback) {that.setRemotePower(value, callback);}); 243 | 244 | service 245 | .addCharacteristic(Characteristic.Brightness) 246 | .on('get', function(callback) { that.getLan("brightness", callback);}) 247 | .on('set', function(value, callback) { that.setRemoteColor("brightness", value, callback);}); 248 | 249 | if (this.capabilities.has_color == true) { 250 | service 251 | .addCharacteristic(Characteristic.Hue) 252 | .on('get', function(callback) { that.getLan("hue", callback);}) 253 | .on('set', function(value, callback) { that.setRemoteColor("hue", value, callback);}); 254 | 255 | service 256 | .addCharacteristic(Characteristic.Saturation) 257 | .on('get', function(callback) { that.getLan("saturation", callback);}) 258 | .on('set', function(value, callback) { that.setRemoteColor("saturation", value, callback);}); 259 | } 260 | break; 261 | default: 262 | // gets and sets over the remote api 263 | service 264 | .getCharacteristic(Characteristic.On) 265 | .on('get', function(callback) { that.getRemote("power", callback);}) 266 | .on('set', function(value, callback) {that.setRemotePower(value, callback);}); 267 | 268 | service 269 | .addCharacteristic(Characteristic.Brightness) 270 | .on('get', function(callback) { that.getRemote("brightness", callback);}) 271 | .on('set', function(value, callback) { that.setRemoteColor("brightness", value, callback);}); 272 | 273 | if (this.capabilities.has_color == true) { 274 | service 275 | .addCharacteristic(Characteristic.Hue) 276 | .on('get', function(callback) { that.getRemote("hue", callback);}) 277 | .on('set', function(value, callback) { that.setRemoteColor("hue", value, callback);}); 278 | 279 | service 280 | .addCharacteristic(Characteristic.Saturation) 281 | .on('get', function(callback) { that.getRemote("saturation", callback);}) 282 | .on('set', function(value, callback) { that.setRemoteColor("saturation", value, callback);}); 283 | } 284 | } 285 | 286 | services.push(service); 287 | 288 | service = new Service.AccessoryInformation(); 289 | 290 | service 291 | .setCharacteristic(Characteristic.Manufacturer, "LIFX") 292 | .setCharacteristic(Characteristic.Model, this.model) 293 | .setCharacteristic(Characteristic.SerialNumber, this.serial); 294 | 295 | services.push(service); 296 | 297 | return services; 298 | } 299 | } 300 | 301 | module.exports.accessory = LIFxBulbAccessory; 302 | module.exports.platform = LIFxPlatform; 303 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/config-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge", 4 | "username": "CC:22:3D:E3:CE:30", 5 | "port": 51826, 6 | "pin": "031-45-154" 7 | }, 8 | 9 | "description": "This is an example configuration file with all supported devices. You can use this as a template for creating your own configuration file containing devices you actually own.", 10 | 11 | "platforms": [ 12 | { 13 | "platform" : "Nest", 14 | "name" : "Nest", 15 | "username" : "username", 16 | "password" : "password" 17 | }, 18 | { 19 | "platform" : "TelldusLive", 20 | "name" : "Telldus Live!", 21 | "public_key" : "telldus public key", 22 | "private_key" : "telldus private key", 23 | "token" : "telldus token", 24 | "token_secret" : "telldus token secret" 25 | }, 26 | { 27 | "platform" : "Telldus", 28 | "name" : "Telldus" 29 | }, 30 | { 31 | "platform": "Wink", 32 | "name": "Wink", 33 | "client_id": "YOUR_WINK_API_CLIENT_ID", 34 | "client_secret": "YOUR_WINK_API_CLIENT_SECRET", 35 | "username": "your@email.com", 36 | "password": "WINK_PASSWORD" 37 | }, 38 | { 39 | "platform": "SmartThings", 40 | "name": "SmartThings", 41 | "app_id": "JSON SmartApp Id", 42 | "access_token": "JSON SmartApp AccessToken" 43 | }, 44 | { 45 | "platform": "Domoticz", 46 | "name": "Domoticz", 47 | "server": "127.0.0.1", 48 | "port": "8080", 49 | "roomid": 0, 50 | "loadscenes": 1 51 | }, 52 | { 53 | "platform": "PhilipsHue", 54 | "name": "Phillips Hue", 55 | "username": "" 56 | }, 57 | { 58 | "platform": "ISY", 59 | "name": "ISY", 60 | "host": "192.168.1.20", 61 | "port": "8000", 62 | "username": "username", 63 | "password": "password" 64 | }, 65 | { 66 | "platform": "LogitechHarmony", 67 | "name": "Living Room Harmony Hub" 68 | }, 69 | { 70 | "platform": "Sonos", 71 | "name": "Sonos", 72 | "play_volume": 25 73 | }, 74 | { 75 | "platform": "YamahaAVR", 76 | "play_volume": -35, 77 | "setMainInputTo": "AirPlay" 78 | }, 79 | { 80 | "platform": "ZWayServer", 81 | "url": "http://192.168.1.10:8083/", 82 | "login": "zwayusername", 83 | "password": "zwayuserpassword", 84 | "poll_interval": 2, 85 | "split_services": false 86 | }, 87 | { 88 | "platform": "MiLight", 89 | "name": "MiLight", 90 | "ip_address": "255.255.255.255", 91 | "port": 8899, 92 | "type": "rgbw", 93 | "delay": 30, 94 | "repeat": 3, 95 | "zones":["Kitchen Lamp","Bedroom Lamp","Living Room Lamp","Hallway Lamp"] 96 | }, 97 | { 98 | "platform": "HomeAssistant", 99 | "name": "HomeAssistant", 100 | "host": "http://192.168.1.10:8123", 101 | "password": "XXXXX", 102 | "supported_types": ["light", "switch", "media_player", "scene"] 103 | }, 104 | { 105 | "platform": "LIFx", 106 | "name": "LIFx", 107 | "access_token": "XXXXXXXX generate at https://cloud.lifx.com/settings" 108 | }, 109 | { 110 | "platform": "Netatmo", 111 | "name": "Netatmo Weather", 112 | "auth": { 113 | "client_id": "XXXXX Create at https://dev.netatmo.com/", 114 | "client_secret": "XXXXX Create at https://dev.netatmo.com/", 115 | "username": "your netatmo username", 116 | "password": "your netatmo password" 117 | } 118 | }, 119 | { 120 | "platform": "HomeMatic", 121 | "name": "HomeMatic CCU", 122 | "ccu_ip": "192.168.0.100", 123 | "filter_device":[], 124 | "filter_channel":["BidCos-RF.KEQXXXXXXX:4", "BidCos-RF.LEQXXXXXXX:2"], 125 | "outlets":[ "BidCos-RF.KEQXXXXXXX:4","BidCos-RF.IEQXXXXXXX:1"] 126 | }, 127 | ], 128 | 129 | "accessories": [ 130 | { 131 | "accessory": "WeMo", 132 | "name": "Coffee Maker", 133 | "description": "This shim supports Belkin WeMo devices on the same network as this server. You can create duplicate entries for this device and change the 'name' attribute to reflect what device is plugged into the WeMo, for instance 'Air Conditioner' or 'Coffee Maker'. This name will be used by Siri. Make sure to update the 'wemo_name' attribute with the EXACT name of the device in the WeMo app itself. This can be the same value as 'name' but it doesn't have to be.", 134 | "wemo_name": "CoffeeMaker" 135 | }, 136 | { 137 | "accessory": "LiftMaster", 138 | "name": "Garage Door", 139 | "description": "This shim supports LiftMaster garage door openers that are already internet-connected to the 'MyQ' service.", 140 | // "requiredDeviceId", "", 141 | "username": "your-liftmaster-username", 142 | "password" : "your-liftmaster-password" 143 | }, 144 | { 145 | "accessory": "Lockitron", 146 | "name": "Front Door", 147 | "description": "This shim supports Lockitron locks. It uses the Lockitron cloud API, so the Lockitron must be 'awake' for locking and unlocking to actually happen. You can wake up Lockitron after issuing an lock/unlock command by knocking on the door.", 148 | "lock_id": "your-lock-id", 149 | "api_token" : "your-lockitron-api-access-token" 150 | }, 151 | { 152 | "accessory": "Carwings", 153 | "name": "Leaf", 154 | "description": "This shim supports controlling climate control on Nissan cars with Carwings. Note that Carwings is super slow and it may take up to 5 minutes for your command to be processed by the Carwings system.", 155 | "username": "your-carwings-username", 156 | "password" : "your-carwings-password" 157 | }, 158 | { 159 | "accessory": "iControl", 160 | "name": "Xfinity Home", 161 | "description": "This shim supports iControl-based security systems like Xfinity Home.", 162 | "system": "XFINITY_HOME", 163 | "email": "your-comcast-email", 164 | "password": "your-comcast-password", 165 | "pin": "your-security-system-pin-code" 166 | }, 167 | { 168 | "accessory": "HomeMatic", 169 | "name": "Light", 170 | "description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)", 171 | "ccu_id": "The XMP-API id of your HomeMatic device", 172 | "ccu_ip": "The IP-Adress of your HomeMatic CCU device" 173 | }, 174 | { 175 | "accessory": "HomeMaticWindow", 176 | "name": "Contact", 177 | "description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)", 178 | "ccu_id": "The XMP-API id of your HomeMatic device (type HM-Sec-RHS)", 179 | "ccu_ip": "The IP-Adress of your HomeMatic CCU device" 180 | }, 181 | { 182 | "accessory": "HomeMaticThermo", 183 | "name": "Contact", 184 | "description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)", 185 | "ccu_id_TargetTemp": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", 186 | "ccu_id_CurrentTemp": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", 187 | "ccu_id_ControlMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", 188 | "ccu_id_ManuMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", 189 | "ccu_id_AutoMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )", 190 | "ccu_ip": "The IP-Adress of your HomeMatic CCU device" 191 | }, 192 | { 193 | "accessory": "X10", 194 | "name": "Lamp", 195 | "ip_address": "localhost:3000", 196 | "device_id": "E1", 197 | "protocol": "pl", 198 | "can_dim": true 199 | }, 200 | { 201 | "accessory": "Http", 202 | "name": "Kitchen Lamp", 203 | "on_url": "https://192.168.1.22:3030/devices/23222/on", 204 | "on_body": "{\"state\":\"On\"}", 205 | "off_url": "https://192.168.1.22:3030/devices/23222/off", 206 | "off_body": "{\"state\":\"Off\"}", 207 | "brightness_url": "https://192.168.1.22:3030/devices/23222/brightness/%b", 208 | "username": "", 209 | "password": "", 210 | "http_method": "POST", 211 | "service": "Switch", 212 | "brightnessHandling": "no" 213 | }, 214 | { 215 | "accessory": "HttpHygrometer", 216 | "name": "Kitchen", 217 | "url": "http://host/URL", 218 | "http_method": "GET" 219 | }, 220 | { 221 | "accessory": "HttpThermometer", 222 | "name": "Garage", 223 | "url": "http://home/URL", 224 | "http_method": "GET" 225 | }, 226 | { 227 | "accessory": "ELKM1", 228 | "name": "Security System", 229 | "description": "Allows basic control of Elk M1 security system. You can use 1 of 3 arm modes: Away, Stay, Night. If you need to access all 3, create 3 accessories with different names.", 230 | "zone": "1", 231 | "host": "192.168.1.10", 232 | "port": "2101", 233 | "pin": "1234", 234 | "arm": "Away" 235 | }, 236 | { 237 | "accessory": "AD2USB", 238 | "name": "Alarm", 239 | "description": "Arm, disarm, and status monitoring of the default partition for Honeywell/Ademco alarm systems. Requires network configured AD2USB interface", 240 | "host": "192.168.1.200", // IP address of the SER2SOCK service 241 | "port" : 4999, // Port the SER2SOCK process is running on 242 | "pin": "1234" // PIN used for arming / disarming 243 | }, 244 | { 245 | "accessory": "Tesla", 246 | "name": "Tesla", 247 | "description": "This shim supports controlling climate control on the Tesla Model S.", 248 | "username": "tesla_email", 249 | "password" : "tesla_password" 250 | }, 251 | { 252 | "accessory": "Hyperion", 253 | "name": "TV Backlight", 254 | "description": "Control the Hyperion TV backlight server. https://github.com/tvdzwan/hyperion", 255 | "host": "localhost", 256 | "port": "19444" 257 | }, 258 | { 259 | "accessory": "mpdclient", 260 | "name" : "mpd", 261 | "host" : "localhost", 262 | "port" : 6600, 263 | "description": "Allows some control of an MPD server" 264 | }, 265 | { 266 | "accessory": "FileSensor", 267 | "name": "File Time Motion Sensor", 268 | "path": "/tmp/CameraDump/", 269 | "window_seconds": 5, 270 | "sensor_type": "m", 271 | "inverse": false 272 | }, 273 | { 274 | "accessory": "GenericRS232Device", 275 | "name": "Projector", 276 | "description": "Make sure you set a 'Siri-Name' for your iOS-Device (example: 'Home Cinema') otherwise it might not work.", 277 | "id": "TYDYMU044UVNP", 278 | "baudrate": 9600, 279 | "device": "/dev/tty.usbserial", 280 | "manufacturer": "Acer", 281 | "model_name": "H6510BD", 282 | "on_command": "* 0 IR 001\r", 283 | "off_command": "* 0 IR 002\r" 284 | } 285 | ] 286 | } 287 | -------------------------------------------------------------------------------- /homebridge-legacy-plugins/platforms/ISY.js: -------------------------------------------------------------------------------- 1 | var types = require("../api").homebridge.hapLegacyTypes; 2 | var xml2js = require('xml2js'); 3 | var request = require('request'); 4 | var util = require('util'); 5 | 6 | var parser = new xml2js.Parser(); 7 | 8 | 9 | var power_state_ctype = { 10 | cType: types.POWER_STATE_CTYPE, 11 | onUpdate: function(value) { return; }, 12 | perms: ["pw","pr","ev"], 13 | format: "bool", 14 | initialValue: 0, 15 | supportEvents: true, 16 | supportBonjour: false, 17 | manfDescription: "Change the power state", 18 | designedMaxLength: 1 19 | }; 20 | 21 | function ISYURL(user, pass, host, port, path) { 22 | return util.format("http://%s:%s@%s:%d%s", user, pass, host, port, encodeURI(path)); 23 | } 24 | 25 | function ISYPlatform(log, config) { 26 | this.host = config["host"]; 27 | this.port = config["port"]; 28 | this.user = config["username"]; 29 | this.pass = config["password"]; 30 | 31 | this.log = log; 32 | } 33 | 34 | ISYPlatform.prototype = { 35 | accessories: function(callback) { 36 | this.log("Fetching ISY Devices."); 37 | 38 | var that = this; 39 | var url = ISYURL(this.user, this.pass, this.host, this.port, "/rest/nodes"); 40 | 41 | var options = { 42 | url: url, 43 | method: 'GET' 44 | }; 45 | 46 | var foundAccessories = []; 47 | 48 | request(options, function(error, response, body) { 49 | if (error) 50 | { 51 | console.trace("Requesting ISY devices."); 52 | that.log(error); 53 | return error; 54 | } 55 | 56 | parser.parseString(body, function(err, result) { 57 | result.nodes.node.forEach(function(obj) { 58 | var enabled = obj.enabled[0] == 'true'; 59 | 60 | if (enabled) 61 | { 62 | var device = new ISYAccessory( 63 | that.log, 64 | that.host, 65 | that.port, 66 | that.user, 67 | that.pass, 68 | obj.name[0], 69 | obj.address[0], 70 | obj.property[0].$.uom 71 | ); 72 | 73 | foundAccessories.push(device); 74 | } 75 | }); 76 | }); 77 | 78 | callback(foundAccessories.sort(function (a,b) { 79 | return (a.name > b.name) - (a.name < b.name); 80 | })); 81 | }); 82 | } 83 | } 84 | 85 | function ISYAccessory(log, host, port, user, pass, name, address, uom) { 86 | this.log = log; 87 | this.host = host; 88 | this.port = port; 89 | this.user = user; 90 | this.pass = pass; 91 | this.name = name; 92 | this.address = address; 93 | this.uom = uom; 94 | } 95 | 96 | ISYAccessory.prototype = { 97 | query: function() { 98 | var path = util.format("/rest/status/%s", encodeURI(this.address)); 99 | var url = ISYURL(this.user, this.pass, this.host, this.port, path); 100 | 101 | var options = { url: url, method: 'GET' }; 102 | request(options, function(error, response, body) { 103 | if (error) 104 | { 105 | console.trace("Requesting Device Status."); 106 | that.log(error); 107 | return error; 108 | } 109 | 110 | parser.parseString(body, function(err, result) { 111 | var value = result.properties.property[0].$.value; 112 | return value; 113 | }); 114 | 115 | }); 116 | }, 117 | 118 | command: function(c, value) { 119 | this.log(this.name + " sending command " + c + " with value " + value); 120 | 121 | switch (c) 122 | { 123 | case 'On': 124 | path = "/rest/nodes/" + this.address + "/cmd/DFON"; 125 | break; 126 | case 'Off': 127 | path = "/rest/nodes/" + this.address + "/cmd/DFOF"; 128 | break; 129 | case 'Low': 130 | path = "/rest/nodes/" + this.address + "/cmd/DON/85"; 131 | break; 132 | case 'Medium': 133 | path = "/rest/nodes/" + this.address + "/cmd/DON/128"; 134 | break; 135 | case 'High': 136 | path = "/rest/nodes/" + this.address + "/cmd/DON/255"; 137 | break; 138 | case 'setLevel': 139 | if (value > 0) 140 | { 141 | path = "/rest/nodes/" + this.address + "/cmd/DON/" + Math.floor(255 * (value / 100)); 142 | } 143 | break; 144 | default: 145 | this.log("Unimplemented command sent to " + this.name + " Command " + c); 146 | break; 147 | } 148 | 149 | if (path) 150 | { 151 | var url = ISYURL(this.user, this.pass, this.host, this.port, path); 152 | var options = { 153 | url: url, 154 | method: 'GET' 155 | }; 156 | 157 | var that = this; 158 | request(options, function(error, response, body) { 159 | if (error) 160 | { 161 | console.trace("Sending Command."); 162 | that.log(error); 163 | return error; 164 | } 165 | that.log("Sent command " + path + " to " + that.name); 166 | }); 167 | } 168 | }, 169 | 170 | informationCharacteristics: function() { 171 | return [ 172 | { 173 | cType: types.NAME_CTYPE, 174 | onUpdate: null, 175 | perms: ["pr"], 176 | format: "string", 177 | initialValue: this.name, 178 | supportEvents: false, 179 | supportBonjour: false, 180 | manfDescription: "Name of the accessory", 181 | designedMaxLength: 255 182 | },{ 183 | cType: types.MANUFACTURER_CTYPE, 184 | onUpdate: null, 185 | perms: ["pr"], 186 | format: "string", 187 | initialValue: "SmartHome", 188 | supportEvents: false, 189 | supportBonjour: false, 190 | manfDescription: "Manufacturer", 191 | designedMaxLength: 255 192 | },{ 193 | cType: types.MODEL_CTYPE, 194 | onUpdate: null, 195 | perms: ["pr"], 196 | format: "string", 197 | initialValue: "Rev-1", 198 | supportEvents: false, 199 | supportBonjour: false, 200 | manfDescription: "Model", 201 | designedMaxLength: 255 202 | },{ 203 | cType: types.SERIAL_NUMBER_CTYPE, 204 | onUpdate: null, 205 | perms: ["pr"], 206 | format: "string", 207 | initialValue: this.address, 208 | supportEvents: false, 209 | supportBonjour: false, 210 | manfDescription: "SN", 211 | designedMaxLength: 255 212 | },{ 213 | cType: types.IDENTIFY_CTYPE, 214 | onUpdate: null, 215 | perms: ["pw"], 216 | format: "bool", 217 | initialValue: false, 218 | supportEvents: false, 219 | supportBonjour: false, 220 | manfDescription: "Identify Accessory", 221 | designedMaxLength: 1 222 | } 223 | ] 224 | }, 225 | 226 | controlCharacteristics: function(that) { 227 | cTypes = [{ 228 | cType: types.NAME_CTYPE, 229 | onUpdate: null, 230 | perms: ["pr"], 231 | format: "string", 232 | initialValue: this.name, 233 | supportEvents: true, 234 | supportBonjour: false, 235 | manfDescription: "Name of service", 236 | designedMaxLength: 255 237 | }] 238 | 239 | if (this.uom == "%/on/off") { 240 | cTypes.push({ 241 | cType: types.POWER_STATE_CTYPE, 242 | perms: ["pw","pr","ev"], 243 | format: "bool", 244 | initialValue: 0, 245 | supportEvents: true, 246 | supportBonjour: false, 247 | manfDescription: "Change the power state", 248 | designedMaxLength: 1, 249 | onUpdate: function(value) { 250 | if (value == 0) { 251 | that.command("Off") 252 | } else { 253 | that.command("On") 254 | } 255 | }, 256 | onRead: function() { 257 | return this.query(); 258 | } 259 | }); 260 | cTypes.push({ 261 | cType: types.BRIGHTNESS_CTYPE, 262 | perms: ["pw","pr","ev"], 263 | format: "int", 264 | initialValue: 0, 265 | supportEvents: true, 266 | supportBonjour: false, 267 | manfDescription: "Adjust Brightness of Light", 268 | designedMinValue: 0, 269 | designedMaxValue: 100, 270 | designedMinStep: 1, 271 | unit: "%", 272 | onUpdate: function(value) { 273 | that.command("setLevel", value); 274 | }, 275 | onRead: function() { 276 | var val = this.query(); 277 | that.log("Query: " + val); 278 | return val; 279 | } 280 | }); 281 | } 282 | else if (this.uom == "off/low/med/high") 283 | { 284 | cTypes.push({ 285 | cType: types.POWER_STATE_CTYPE, 286 | perms: ["pw","pr","ev"], 287 | format: "bool", 288 | initialValue: 0, 289 | supportEvents: true, 290 | supportBonjour: false, 291 | manfDescription: "Change the power state", 292 | designedMaxLength: 1, 293 | onUpdate: function(value) { 294 | if (value == 0) { 295 | that.command("Off") 296 | } else { 297 | that.command("On") 298 | } 299 | }, 300 | onRead: function() { 301 | return this.query(); 302 | } 303 | }); 304 | cTypes.push({ 305 | cType: types.ROTATION_SPEED_CTYPE, 306 | perms: ["pw","pr","ev"], 307 | format: "bool", 308 | initialValue: 0, 309 | supportEvents: true, 310 | supportBonjour: false, 311 | manfDescription: "Change the speed of the fan", 312 | designedMaxLength: 1, 313 | onUpdate: function(value) { 314 | if (value == 0) { 315 | that.command("Off"); 316 | } else if (value > 0 && value < 40) { 317 | that.command("Low"); 318 | } else if (value > 40 && value < 75) { 319 | that.command("Medium"); 320 | } else { 321 | that.command("High"); 322 | } 323 | }, 324 | onRead: function() { 325 | return this.query(); 326 | } 327 | }); 328 | } 329 | else if (this.uom == "on/off") 330 | { 331 | cTypes.push({ 332 | cType: types.POWER_STATE_CTYPE, 333 | perms: ["pw","pr","ev"], 334 | format: "bool", 335 | initialValue: 0, 336 | supportEvents: true, 337 | supportBonjour: false, 338 | manfDescription: "Change the power state", 339 | designedMaxLength: 1, 340 | onUpdate: function(value) { 341 | if (value == 0) { 342 | that.command("Off") 343 | } else { 344 | that.command("On") 345 | } 346 | }, 347 | onRead: function() { 348 | return this.query(); 349 | } 350 | }); 351 | } 352 | 353 | return cTypes; 354 | }, 355 | 356 | sType: function() { 357 | if (this.uom == "%/on/off") { 358 | return types.LIGHTBULB_STYPE; 359 | } else if (this.uom == "on/off") { 360 | return types.SWITCH_STYPE; 361 | } else if (this.uom == "off/low/med/high") { 362 | return types.FAN_STYPE; 363 | } 364 | 365 | return types.SWITCH_STYPE; 366 | }, 367 | 368 | getServices: function() { 369 | var that = this; 370 | var services = [{ 371 | sType: types.ACCESSORY_INFORMATION_STYPE, 372 | characteristics: this.informationCharacteristics(), 373 | }, 374 | { 375 | sType: this.sType(), 376 | characteristics: this.controlCharacteristics(that) 377 | }]; 378 | 379 | //that.log("Loaded services for " + that.name); 380 | return services; 381 | } 382 | }; 383 | 384 | module.exports.accessory = ISYAccessory; 385 | module.exports.platform = ISYPlatform; 386 | --------------------------------------------------------------------------------