diff --git a/.gitignore b/.gitignore index 81a1589..e7bf1ac 100644 --- a/.gitignore +++ b/.gitignore @@ -4,13 +4,3 @@ # Node node_modules/ -npm-debug.log -.node-version - -# Intellij -.idea/ -*.iml - -# HomeBridge -config.json -persist/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md index 55f4e85..60ff061 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,4 @@ -# HomeBridge +# Branch in Progress -HomeBridge is a lightweight NodeJS server you can run on your home network that emulates the iOS HomeKit API. It includes a set of "shims" (found in the [accessories](accessories/) and [platforms](platforms/) folders) that provide a basic bridge from HomeKit to various 3rd-party APIs provided by manufacturers of "smart home" devices. - -Since Siri supports devices added through HomeKit, this means that with HomeBridge you can ask Siri to control devices that don't have any support for HomeKit at all. For instance, using the included shims, you can say things like: - - * _Siri, unlock the front door._ ([Lockitron](https://lockitron.com)) - * _Siri, open the garage door._ ([LiftMaster MyQ](https://www.myliftmaster.com)) - * _Siri, turn on the Leaf._ ([Carwings](http://www.nissanusa.com/innovations/carwings.article.html)) - * _Siri, turn off the Speakers._ ([Sonos](http://www.sonos.com)) - * _Siri, turn on the Dehumidifier._ ([WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/)) - * _Siri, turn on Away Mode._ ([Xfinity Home](http://www.comcast.com/home-security.html)) - * _Siri, turn on the living room lights._ ([Wink](http://www.wink.com), [SmartThings](http://www.smartthings.com), [X10](http://github.com/edc1591/rest-mochad), [Philips Hue](http://meethue.com)) - -If you would like to support any other devices, please write a shim and create a pull request and I'd be happy to add it to this official list. - -# Shim types -There are 2 types of shims supported in HomeBridge. - -* Accessory - Individual device -* Platform - A full bridge to another system - -## Accessories - -Accessories are individual devices you would like to bridge to HomeKit. You set them up by declaring them individually in your `config.json` file. Generally, you specify them by `name` or `id` and which system they use. - -## Platforms - -Platforms bridge entire systems to HomeKit. Platforms can be things like Wink or SmartThings or Vera. By adding a platform to your `config.json`, HomeBridge will automatically detect all of your devices for you. - -All you have to do is add the right config options so HomeBridge can authenticate and communicate with your other system, and voila, your devices will be available to HomeKit via HomeBridge. - -# Why? - -Technically, the device manufacturers should be the ones implementing the HomeKit API. And I'm sure they will - eventually. When they do, these shims will be obsolete, and I hope that happens soon. In the meantime, this server is a fun way to get a taste of the future, for those who just can't bear to wait until "real" HomeKit devices are on the market. - -# Credit - -HomeBridge itself is basically just a set of shims and a README. The actual HomeKit API work was done by [KhaosT](http://twitter.com/khaost) in his [HAP-NodeJS](https://github.com/KhaosT/HAP-NodeJS) project. Additionally, many of the shims benefit from amazing NodeJS projects out there like `sonos` and `wemo` that implement all the interesting functionality. - -# Before you Begin - -I would call this project a "novelty" in its current form, and is for **intrepid hackers only**. To make any of this work, you'll need: - - * An app on your iOS device that can manage your HomeKit database. - * An always-running server (like a Raspberry Pi) on which you can install NodeJS. - * Knowledge of Git submodules and npm. - -You'll also need some patience, as Siri can be very strict about sentence structure, and occasionally she will forget about HomeKit altogether. But it's not surprising that HomeKit isn't rock solid, since almost no one can actually use it today besides developers who are creating hardware accessories for it. There are, to my knowledge, exactly zero licensed HomeKit devices on the market right now, so Apple can easily get away with this all being a work in progress. - -Additionally, the shims I've created implement the bare minimum of HomeKit needed to provide basic functionality like turning things off and on. I haven't written any kind of good feedback or error handling, and although they support changing state, they don't support reading the current state, so if you ask questions like "Is my door unlocked?" Siri will respond with the default of "Nope!" no matter what. - -# Getting Started - -OK, if you're still excited enough about ordering Siri to make your coffee (which, who wouldn't be!) then here's how to set things up. First, clone this repo: - - $ git clone https://github.com/nfarina/homebridge.git - $ cd homebridge - $ npm install - -**Node**: You'll need to have NodeJS version 0.12.x or better installed for required submodule `HAP-NodeJS` to load. - -Now you should be able to run the homebridge server: - - $ cd homebridge - $ npm run start - Starting HomeBridge server... - Couldn't find a config.json file [snip] - -The server won't do anything until you've created a `config.json` file containing your home devices (or _accessories_ in HomeKit parlance) or platforms you wish to make available to iOS. You can start by copying and modifying the included `config-sample.json` file which includes declarations for all supported accessories and platforms. - -Once you've added your devices and/or platforms, you should be able to run the server again and see them initialize: - - $ npm run start - Starting HomeBridge server... - Loading 6 accessories... - [Speakers] Initializing 'Sonos' accessory... - [Coffee Maker] Initializing 'WeMo' accessory... - [Speakers] Initializing 'Sonos' accessory... - [Coffee Maker] Initializing 'WeMo' accessory... - [Wink] Initializing Wink platform... - [Wink] Fetching Wink devices. - [Wink] Initializing device with name Living Room Lamp... - -Your server is now ready to receive commands from iOS. - -# Adding your devices to iOS - -HomeKit is actually not an app; it's a "database" similar to HealthKit and PassKit. But where HealthKit has the companion _Health_ app and PassKit has _Passbook_, Apple has supplied no app for managing your HomeKit database (at least [not yet](http://9to5mac.com/2015/05/20/apples-planned-ios-9-home-app-uses-virtual-rooms-to-manage-homekit-accessories/)). However, the HomeKit API is open for developers to write their own apps for adding devices to HomeKit. - -Fortunately, there are now a few apps in the App Store that can manage your HomeKit devices. Try [Insteon+](https://itunes.apple.com/US/app/id919270334?mt=8) or [Lutron](https://itunes.apple.com/us/app/lutron-app-for-caseta-wireless/id886753021?mt=8) or a number of others. - -## Adding HomeKit Accessories - -Once you've gotten a HomeKit app running on your iOS device, you can begin adding accessories. The app should "discover" the accessories defined in your `config.json` file, assuming that you're still running the HomeBridge server and you're on the same Wifi network. - -When you attempt to add a device, it will ask for a "PIN code". The default code for _all_ HomeBridge accessories is `031-45-154`. Adding the device should create some files in the `persist` directory of the HomeBridge server, which stores the pairing relationship. - -# Interacting with your Devices - -Once your device has been added to HomeKit, you should be able to tell Siri to control your devices. However, realize that Siri is a cloud service, and iOS may need some time to synchronize your device information with iCloud. - -Also, keep in mind HomeKit is not very robust yet, and it is common for it to fail intermittently ("Sorry, I wasn't able to control your devices" etc.) then start working again for no reason. Also I've noticed that it will get cranky and stop working altogether sometimes. The usual voodoo applies here: reboot your device, restart the homebridge server, run your HomeKit iOS app and poke around, etc. - -One final thing to remember is that Siri will almost always prefer its default phrase handling over HomeKit devices. For instance, if you name your Sonos device "Radio" and try saying "Siri, turn on the Radio" then Siri will probably start playing an iTunes Radio station on your phone. Even if you name it "Sonos" and say "Siri, turn on the Sonos", Siri will probably just launch the Sonos app instead. This is why, for instance, the suggested `name` for the Sonos shim in `config-samples.json` is "Speakers". - -# Final Notes - -HomeKit is definitely amazing when it works. Speaking to Siri is often much quicker and easier than launching whatever app your device manufacturer provides. - -I welcome any suggestions or pull requests, but keep in mind that it's likely not possible to support all the things you might want to do with a device through HomeKit. For instance, you might want to hack the Sonos shim to play the specific kind of music you want and that's great, but it might not be appropriate to merge those specific changes into this repository. The shims here should be mostly simple "canonical examples" and easily hackable by others. - -Good luck! +This branch contains an in-progress ground-up rewrite of HomeBridge that looks more like what we want in the [roadmap](/nfarina/homebridge/wiki/Roadmap). \ No newline at end of file diff --git a/accessories/AD2USB.js b/accessories/AD2USB.js deleted file mode 100644 index ea05377..0000000 --- a/accessories/AD2USB.js +++ /dev/null @@ -1,245 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var AD2USB = require('ad2usb'); -var CUSTOM_PANEL_LCD_TEXT_CTYPE = "A3E7B8F9-216E-42C1-A21C-97D4E3BE52C8"; - -function AD2USBAccessory(log, config) { - - this.log = log; - this.name = config["name"]; - this.host = config["host"]; - this.port = config["port"]; - this.pin = config["pin"]; - var that = this; - this.currentArmState = 2; - this.currentStateCharacteristic = undefined; - this.targetStateCharacteristic = undefined; - this.lcdCharacteristic = undefined; - - var alarm = AD2USB.connect(this.host, this.port, function() { - - // Send an initial empty character to get status - alarm.send(''); - - // Armed Away - alarm.on('armedAway', function() { - - that.log("Armed to AWAY"); - if (that.currentStateCharacteristic) { - that.currentStateCharacteristic.updateValue(0, null); - } - if (that.targetStateCharacteristic) { - that.targetStateCharacteristic.updateValue(1, null); - } - - }); - - // Armed Stay - alarm.on('armedStay', function() { - - that.log("Armed to STAY"); - if (that.currentStateCharacteristic) { - that.currentStateCharacteristic.updateValue(0, null); - } - if (that.targetStateCharacteristic) { - that.targetStateCharacteristic.updateValue(0, null); - } - - }); - - // Armed Night - alarm.on('armedNight', function() { - - that.log("Armed to NIGHT"); - if (that.currentStateCharacteristic) { - that.currentStateCharacteristic.updateValue(0, null); - } - if (that.targetStateCharacteristic) { - that.targetStateCharacteristic.updateValue(2, null); - } - - }); - - // Disarmed - alarm.on('disarmed', function() { - - that.log("Disarmed"); - if (that.currentStateCharacteristic) { - that.currentStateCharacteristic.updateValue(1, null); - } - if (that.targetStateCharacteristic) { - that.targetStateCharacteristic.updateValue(3, null); - } - - }); - - // Text Change - alarm.on('lcdtext', function(newText) { - - that.log("LCD: " + newText); - if (that.lcdCharacteristic) { - that.lcdCharacteristic.updateValue(newText, null); - } - - }); - - - }); - this.alarm = alarm; - -} - -AD2USBAccessory.prototype = { - - setArmState: function(targetArmState) { - - var that = this; - that.log("Desired target arm state: " + targetArmState); - - // TARGET - // 0 - Stay - // 1 - Away - // 2 - Night - // 3 - Disarm - if (targetArmState == 0) { - that.alarm.armStay(that.pin); - } - else if (targetArmState == 1) { - that.alarm.armAway(that.pin); - } - else if (targetArmState == 2) { - that.alarm.armNight(that.pin); - } - else if (targetArmState == 3) { - that.alarm.disarm(that.pin); - } - - - // CURRENT - // 0 - Armed - // 1 - Disarmed - // 2 - Hold - - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Nutech", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "AD2USB", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "AD2USBIF", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.ALARM_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.ALARM_CURRENT_STATE_CTYPE, - onUpdate: null, - onRegister: function(characteristic) { - - that.currentStateCharacteristic = characteristic; - characteristic.eventEnabled = true; - - }, - perms: ["pr","ev"], - format: "int", - initialValue: 2, - supportEvents: true, - supportBonjour: false, - manfDescription: "Alarm current arm state", - designedMaxLength: 1 - },{ - cType: types.ALARM_TARGET_STATE_CTYPE, - onUpdate: function(value) { that.setArmState(value); }, - onRegister: function(characteristic) { - - that.targetStateCharacteristic = characteristic; - characteristic.eventEnabled = true; - - }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 1, - supportEvents: true, - supportBonjour: false, - manfDescription: "Alarm target arm state", - designedMaxLength: 1 - }, - { - cType: CUSTOM_PANEL_LCD_TEXT_CTYPE, - onUpdate: null, - onRegister: function(characteristic) { - - that.lcdCharacteristic = characteristic; - characteristic.eventEnabled = true; - - }, - perms: ["pr","ev"], - format: "string", - initialValue: "Unknown", - supportEvents: false, - supportBonjour: false, - manfDescription: "Keypad Text", - designedMaxLength: 64 - }] - }]; - } -}; - -module.exports.accessory = AD2USBAccessory; diff --git a/accessories/Carwings.js b/accessories/Carwings.js deleted file mode 100644 index 150c936..0000000 --- a/accessories/Carwings.js +++ /dev/null @@ -1,126 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var carwings = require("carwingsjs"); - -function CarwingsAccessory(log, config) { - this.log = log; - this.name = config["name"]; - this.username = config["username"]; - this.password = config["password"]; -} - -CarwingsAccessory.prototype = { - - setPowerState: function(powerOn) { - var that = this; - - carwings.login(this.username, this.password, function(err, result) { - if (!err) { - that.vin = result.vin; - that.log("Got VIN: " + that.vin); - - if (powerOn) { - carwings.startClimateControl(that.vin, null, function(err, result) { - if (!err) - that.log("Started climate control."); - else - that.log("Error starting climate control: " + err); - }); - } - else { - carwings.stopClimateControl(that.vin, function(err, result) { - if (!err) - that.log("Stopped climate control."); - else - that.log("Error stopping climate control: " + err); - }); - } - } - else { - that.log("Error logging in: " + err); - } - }); - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Nissan", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.SWITCH_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.setPowerState(value); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Change the power state of the car", - designedMaxLength: 1 - }] - }]; - } -}; - -module.exports.accessory = CarwingsAccessory; diff --git a/accessories/ELKM1.js b/accessories/ELKM1.js deleted file mode 100644 index d32cc1b..0000000 --- a/accessories/ELKM1.js +++ /dev/null @@ -1,126 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var elkington = require("elkington"); - -function ElkM1Accessory(log, config) { - this.log = log; - this.name = config["name"]; - this.zone = config["zone"]; - this.host = config["host"]; - this.port = config["port"]; - this.pin = config["pin"]; - this.arm = config["arm"]; -} - -ElkM1Accessory.prototype = { - setPowerState: function(alarmOn) { - var that = this; - - if (!alarmOn) - { - return; - } - - var elk = elkington.createConnection({ - port: that.port, - host: that.host, - }); - - switch (that.arm) - { - case 'Away': - elk.armAway({area: that.zone, code: that.pin}); - break; - case 'Stay': - elk.armStay({area: that.zone, code: that.pin}); - break; - case 'Night': - elk.armNightInstant({area: that.zone, code: that.pin}); - break; - default: - break; - } - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Elk", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "M1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.SWITCH_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.setPowerState(value); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Alarm the Zone", - designedMaxLength: 1 - }] - }]; - } -}; - -module.exports.accessory = ElkM1Accessory; diff --git a/accessories/HomeMatic.js b/accessories/HomeMatic.js deleted file mode 100755 index ce575b1..0000000 --- a/accessories/HomeMatic.js +++ /dev/null @@ -1,116 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var request = require("request"); - -function HomeMatic(log, config) { - this.log = log; - this.name = config["name"]; - this.ccuID = config["ccu_id"]; - this.ccuIP = config["ccu_ip"]; -} - -HomeMatic.prototype = { - - setPowerState: function(powerOn) { - - var binaryState = powerOn ? 1 : 0; - var that = this; - - this.log("Setting power state of CCU to " + powerOn); - this.log(this.ccuID+ powerOn); - - request.put({ - url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+this.ccuID+"&new_value="+ powerOn, - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - that.log("State change complete."); - } - else { - that.log("Error '"+err+"' setting lock state: " + body); - } - }); - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "WeMo", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.SWITCH_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.setPowerState(value); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Change the power state of a Variable", - designedMaxLength: 1 - }] - }]; - } -}; - -module.exports.accessory = HomeMatic; diff --git a/accessories/Http.js b/accessories/Http.js deleted file mode 100644 index bfd418b..0000000 --- a/accessories/Http.js +++ /dev/null @@ -1,160 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var request = require("request"); - -function HttpAccessory(log, config) { - this.log = log; - - // url info - this.on_url = config["on_url"]; - this.off_url = config["off_url"]; - this.brightness_url = config["brightness_url"]; - this.http_method = config["http_method"]; - - // device info - this.name = config["name"]; -} - -HttpAccessory.prototype = { - - httpRequest: function(url, method, callback) { - request({ - url: url, - method: method - }, - function (error, response, body) { - callback(error, response, body) - }) - }, - - setPowerState: function(powerOn) { - var url; - - if (powerOn) { - url = this.on_url - this.log("Setting power state on the '"+this.name+"' to on"); - }else{ - url = this.off_url - this.log("Setting power state on the '"+this.name+"' to off"); - } - - this.httpRequest(url, this.http_method, function(error, response, body){ - if (error) { - return console.error('http power function failed:', error); - }else{ - return console.log('http power function succeeded!'); - } - }); - - }, - - setBrightness: function(level) { - var url = this.brightness_url.replace("%b", level) - - this.log("Setting brightness on the '"+this.name+"' to " + level); - - this.httpRequest(url, this.http_method, function(error, response, body){ - if (error) { - return console.error('http brightness function failed:', error); - }else{ - return console.log('http brightness function succeeded!'); - } - }); - - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Http", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.LIGHTBULB_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.setPowerState(value); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1 - },{ - cType: types.BRIGHTNESS_CTYPE, - onUpdate: function(value) { that.setBrightness(value); }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Adjust Brightness", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }] - }]; - } -}; - -module.exports.accessory = HttpAccessory; diff --git a/accessories/LiftMaster.js b/accessories/LiftMaster.js deleted file mode 100644 index 8d67b6c..0000000 --- a/accessories/LiftMaster.js +++ /dev/null @@ -1,305 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var request = require("request"); - -// This seems to be the "id" of the official LiftMaster iOS app -var APP_ID = "JVM/G9Nwih5BwKgNCjLxiFUQxQijAebyyg8QUHr7JOrP+tuPb8iHfRHKwTmDzHOu" - -function LiftMasterAccessory(log, config) { - this.log = log; - this.name = config["name"]; - this.username = config["username"]; - this.password = config["password"]; - this.requiredDeviceId = config["requiredDeviceId"]; -} - -LiftMasterAccessory.prototype = { - - setState: function(state) { - this.targetState = state; - this.login(); - }, - - login: function() { - var that = this; - - // reset our logged-in state hint until we're logged in - this.deviceId = null; - - // querystring params - var query = { - appId: APP_ID, - username: this.username, - password: this.password, - culture: "en" - }; - - // login to liftmaster - request.get({ - url: "https://myqexternal.myqdevice.com/api/user/validatewithculture", - qs: query - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - - // parse and interpret the response - var json = JSON.parse(body); - that.userId = json["UserId"]; - that.securityToken = json["SecurityToken"]; - that.log("Logged in with user ID " + that.userId); - that.getDevice(); - } - else { - that.log("Error '"+err+"' logging in: " + body); - } - }); - }, - - // find your garage door ID - getDevice: function() { - var that = this; - - // querystring params - var query = { - appId: APP_ID, - SecurityToken: this.securityToken, - filterOn: "true" - }; - - // some necessary duplicated info in the headers - var headers = { - MyQApplicationId: APP_ID, - SecurityToken: this.securityToken - }; - - // request details of all your devices - request.get({ - url: "https://myqexternal.myqdevice.com/api/v4/userdevicedetails/get", - qs: query, - headers: headers - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - - // parse and interpret the response - var json = JSON.parse(body); - var devices = json["Devices"]; - var foundDoors = []; - - // look through the array of devices for an opener - for (var i=0; i 1) { - that.log("WARNING: You have multiple doors on your MyQ account."); - that.log("WARNING: Specify the ID of the door you want to control using the 'requiredDeviceId' property in your config.json file."); - that.log("WARNING: You can have multiple liftmaster accessories to cover your multiple doors"); - - for (var j = 0; j < foundDoors.length; j++) { - that.log("Found Door: " + foundDoors[j]); - } - - throw "FATAL: Please specify which specific door this Liftmaster accessory should control - you have multiples on your account"; - - } - - // Did we get a device ID? - if (that.deviceId) { - that.log("Found an opener with ID " + that.deviceId +". Ready to send command..."); - that.setTargetState(); - } - else - { - that.log("Error: Couldn't find a door device, or the ID you specified isn't associated with your account"); - } - } - else { - that.log("Error '"+err+"' getting devices: " + body); - } - }); - }, - - setTargetState: function() { - - var that = this; - var liftmasterState = (this.targetState + "") == "1" ? "0" : "1"; - - // querystring params - var query = { - appId: APP_ID, - SecurityToken: this.securityToken, - filterOn: "true" - }; - - // some necessary duplicated info in the headers - var headers = { - MyQApplicationId: APP_ID, - SecurityToken: this.securityToken - }; - - // PUT request body - var body = { - AttributeName: "desireddoorstate", - AttributeValue: liftmasterState, - ApplicationId: APP_ID, - SecurityToken: this.securityToken, - MyQDeviceId: this.deviceId - }; - - // send the state request to liftmaster - request.put({ - url: "https://myqexternal.myqdevice.com/api/v4/DeviceAttribute/PutDeviceAttribute", - qs: query, - headers: headers, - body: body, - json: true - }, function(err, response, json) { - - if (!err && response.statusCode == 200) { - - if (json["ReturnCode"] == "0") - that.log("State was successfully set."); - else - that.log("Bad return code: " + json["ReturnCode"]); - that.log("Raw response " + JSON.stringify(json)); - } - else { - that.log("Error '"+err+"' setting door state: " + JSON.stringify(json)); - } - }); - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "LiftMaster", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.GARAGE_DOOR_OPENER_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Garage Door Opener Control", - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.CURRENT_DOOR_STATE_CTYPE, - onUpdate: function(value) { that.log("Update current state to " + value); }, - perms: ["pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "BlaBla", - designedMinValue: 0, - designedMaxValue: 4, - designedMinStep: 1, - designedMaxLength: 1 - },{ - cType: types.TARGET_DOORSTATE_CTYPE, - onUpdate: function(value) { that.setState(value); }, - perms: ["pr","pw","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "BlaBla", - designedMinValue: 0, - designedMaxValue: 1, - designedMinStep: 1, - designedMaxLength: 1 - },{ - cType: types.OBSTRUCTION_DETECTED_CTYPE, - onUpdate: function(value) { that.log("Obstruction detected: " + value); }, - perms: ["pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "BlaBla" - }] - }]; - } -}; - -module.exports.accessory = LiftMasterAccessory; \ No newline at end of file diff --git a/accessories/Lockitron.js b/accessories/Lockitron.js deleted file mode 100644 index b5fd023..0000000 --- a/accessories/Lockitron.js +++ /dev/null @@ -1,196 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var request = require("request"); - -function LockitronAccessory(log, config) { - this.log = log; - this.name = config["name"]; - this.lockID = config["lock_id"]; - this.accessToken = config["api_token"]; -} - -LockitronAccessory.prototype = { - getState: function(callback) { - this.log("Getting current state..."); - - var that = this; - - var query = { - access_token: this.accessToken - }; - - request.get({ - url: "https://api.lockitron.com/v2/locks/"+this.lockID, - qs: query - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - var json = JSON.parse(body); - var state = json.state; // "lock" or "unlock" - var locked = state == "lock" - callback(locked); - } - else { - that.log("Error getting state (status code "+response.statusCode+"): " + err) - callback(undefined); - } - }); - }, - - setState: function(state) { - this.log("Set state to " + state); - - var lockitronState = (state == 1) ? "lock" : "unlock"; - var that = this; - - var query = { - access_token: this.accessToken, - state: lockitronState - }; - - request.put({ - url: "https://api.lockitron.com/v2/locks/"+this.lockID, - qs: query - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - that.log("State change complete."); - } - else { - that.log("Error '"+err+"' setting lock state: " + body); - } - }); - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Apigee", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-2", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.LOCK_MECHANISM_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Lock Mechanism", - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE, - onRead: function(callback) { that.getState(callback); }, - onUpdate: function(value) { that.log("Update current state to " + value); }, - perms: ["pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "BlaBla", - designedMinValue: 0, - designedMaxValue: 3, - designedMinStep: 1, - designedMaxLength: 1 - },{ - cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE, - onUpdate: function(value) { that.setState(value); }, - perms: ["pr","pw","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "BlaBla", - designedMinValue: 0, - designedMaxValue: 1, - designedMinStep: 1, - designedMaxLength: 1 - }] - },{ - sType: types.LOCK_MANAGEMENT_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Lock Management", - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.LOCK_MANAGEMENT_CONTROL_POINT_CTYPE, - onUpdate: function(value) { that.log("Update control point to " + value); }, - perms: ["pw"], - format: "data", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "BlaBla", - designedMaxLength: 255 - },{ - cType: types.VERSION_CTYPE, - onUpdate: function(value) { that.log("Update version to " + value); }, - perms: ["pr"], - format: "string", - initialValue: "1.0", - supportEvents: false, - supportBonjour: false, - manfDescription: "BlaBla", - designedMaxLength: 255 - }] - }]; - } -}; - -module.exports.accessory = LockitronAccessory; \ No newline at end of file diff --git a/accessories/Sonos.js b/accessories/Sonos.js deleted file mode 100644 index 5757b0c..0000000 --- a/accessories/Sonos.js +++ /dev/null @@ -1,146 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var sonos = require('sonos'); - -function SonosAccessory(log, config) { - this.log = log; - this.name = config["name"]; - this.playVolume = config["play_volume"]; - this.device = null; - this.search(); -} - -SonosAccessory.prototype = { - - search: function() { - var that = this; - - sonos.search(function(device) { - that.log("Found device at " + device.host); - - device.deviceDescription(function (err, description) { - - if (description["zoneType"] == '3') { - that.log("Found playable device"); - // device is an instance of sonos.Sonos - that.device = device; - } - - }); - }); - }, - - setPlaying: function(playing) { - - if (!this.device) { - this.log("No device found (yet?)"); - return; - } - - var that = this; - - if (playing) { - this.device.play(function(err, success) { - that.log("Playback attempt with success: " + success); - }); - - if (this.playVolume) { - this.device.setVolume(this.playVolume, function(err, success) { - if (!err) { - that.log("Set volume to " + that.playVolume); - } - else { - that.log("Problem setting volume: " + err); - } - }); - } - } - else { - this.device.stop(function(err, success) { - that.log("Stop attempt with success: " + success); - }); - } - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Sonos", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.SWITCH_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Speakers", - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.setPlaying(value); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Change the playback state of the sonos", - designedMaxLength: 1 - }] - }]; - } -}; - -module.exports.accessory = SonosAccessory; diff --git a/accessories/WeMo.js b/accessories/WeMo.js deleted file mode 100644 index 6684370..0000000 --- a/accessories/WeMo.js +++ /dev/null @@ -1,139 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var wemo = require('wemo'); - -// extend our search timeout from 5 seconds to 60 -wemo.SearchTimeout = 60000; -wemo.timeout = wemo.SearchTimeout // workaround for a bug in wemo.js v0.0.4 - -function WeMoAccessory(log, config) { - this.log = log; - this.name = config["name"]; - this.wemoName = config["wemo_name"]; - this.device = null; - this.log("Searching for WeMo device with exact name '" + this.wemoName + "'..."); - this.search(); -} - -WeMoAccessory.prototype = { - - search: function() { - var that = this; - - wemo.Search(this.wemoName, function(err, device) { - if (!err && device) { - that.log("Found '"+that.wemoName+"' device at " + device.ip); - that.device = new wemo(device.ip, device.port); - } - else { - that.log("Error finding device '" + that.wemoName + "': " + err); - that.log("Continuing search for WeMo device with exact name '" + that.wemoName + "'..."); - that.search(); - } - }); - }, - - setPowerState: function(powerOn) { - - if (!this.device) { - this.log("No '"+this.wemoName+"' device found (yet?)"); - return; - } - - var binaryState = powerOn ? 1 : 0; - var that = this; - - this.log("Setting power state on the '"+this.wemoName+"' to " + binaryState); - - this.device.setBinaryState(binaryState, function(err, result) { - if (!err) { - that.log("Successfully set power state on the '"+that.wemoName+"' to " + binaryState); - } - else { - that.log("Error setting power state on the '"+that.wemoName+"'") - } - }); - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "WeMo", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.SWITCH_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.setPowerState(value); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Change the power state of the WeMo", - designedMaxLength: 1 - }] - }]; - } -}; - -module.exports.accessory = WeMoAccessory; diff --git a/accessories/X10.js b/accessories/X10.js deleted file mode 100644 index 6668a44..0000000 --- a/accessories/X10.js +++ /dev/null @@ -1,151 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var request = require("request"); - -function X10(log, config) { - this.log = log; - this.ip_address = config["ip_address"]; - this.name = config["name"]; - this.deviceID = config["device_id"]; - this.protocol = config["protocol"]; - this.canDim = config["can_dim"]; -} - -X10.prototype = { - - setPowerState: function(powerOn) { - - var binaryState = powerOn ? "on" : "off"; - var that = this; - - this.log("Setting power state of " + this.deviceID + " to " + powerOn); - request.put({ - url: "http://"+this.ip_address+"/x10/"+this.deviceID+"/power/"+binaryState+"?protocol="+this.protocol, - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - that.log("State change complete."); - } - else { - that.log("Error '"+err+"' setting power state: " + body); - } - }); - }, - - setBrightnessLevel: function(value) { - - var that = this; - - this.log("Setting brightness level of " + this.deviceID + " to " + value); - request.put({ - url: "http://"+this.ip_address+"/x10/"+this.deviceID+"/brightness/?protocol="+this.protocol+"&value="+value, - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - that.log("State change complete."); - } - else { - that.log("Error '"+err+"' setting brightness level: " + body); - } - }); - }, - - getServices: function() { - var that = this; - var services = [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "X10", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.LIGHTBULB_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.setPowerState(value); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Change the power state of a Variable", - designedMaxLength: 1 - }] - }]; - if (that.canDim) { - services[1].characteristics.push({ - cType: types.BRIGHTNESS_CTYPE, - onUpdate: function(value) { that.setBrightnessLevel(value); }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: false, - supportBonjour: false, - manfDescription: "Adjust Brightness of Light", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }); - } - return services; - } -}; - -module.exports.accessory = X10; diff --git a/accessories/XfinityHome.js b/accessories/XfinityHome.js deleted file mode 100644 index cafdbd1..0000000 --- a/accessories/XfinityHome.js +++ /dev/null @@ -1,284 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var request = require("request"); -var xmldoc = require("xmldoc"); - -function XfinityHomeAccessory(log, config) { - this.log = log; - this.name = config["name"]; - this.email = config["email"]; - this.password = config["password"]; - this.dsig = config["dsig"]; - this.pinCode = config["pin"]; -} - -XfinityHomeAccessory.prototype = { - - armWithType: function(armed, type) { - this.log("Arming with type " + type + " = " + armed + "..."); - this.targetArmed = armed; - this.targetArmType = type; - this.getLoginToken(); - }, - - getLoginToken: function() { - this.log("Retrieving login token..."); - - var that = this; - - request.post({ - url: "https://login.comcast.net/api/login", - form: { - appkey:"iControl", - dsig: this.dsig, - u: this.email, - p: this.password - } - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - - var doc = new xmldoc.XmlDocument(body); - that.loginToken = doc.valueWithPath("LoginToken"); - that.refreshLoginCookie(); - } - else { - that.log("Error '"+err+"' getting login token: " + body); - } - }); - }, - - refreshLoginCookie: function() { - this.log("Refreshing login cookie..."); - - var that = this; - - request.post({ - url: "https://www.xfinityhomesecurity.com/rest/icontrol/login", - form: { - token: this.loginToken - } - }, function(err, response, body) { - - if (!err && response.statusCode == 200) { - - // extract our "site" from the login response - var json = JSON.parse(body); - that.siteHref = json["login"]["site"]["href"]; - - // manual cookie handling - that.loginCookie = response.headers["set-cookie"]; - - that.getInstances(); - } - else { - that.log("Error '"+err+"' refreshing login cookie: " + body); - } - }); - }, - - getInstances: function() { - this.log("Getting instances for site " + this.siteHref + "..."); - - this.panelHref = null; - var that = this; - - request.get({ - url: "https://www.xfinityhomesecurity.com/"+that.siteHref+"/network/instances", - headers: { Cookie: this.loginCookie }, - json: true - }, function(err, response, json) { - - if (!err && response.statusCode == 200) { - - // extract our "instance" from the response. look for the first "panel" - var instances = json["instances"]["instance"]; - for (var i=0; i= 200 && response.statusCode < 300) { - that.log("Arm response: " + response); - } - else { - that.log("Error '"+err+"' performing arm request: " + body); - } - }); - }, - - getServices: function() { - var that = this; - return [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Comcast", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.SWITCH_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Away Mode", - supportEvents: false, - supportBonjour: false, - manfDescription: "Away Mode service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.armWithType(value, "away"); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Turn on the Away alarm", - designedMaxLength: 1 - }] - },{ - sType: types.SWITCH_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Night Mode", - supportEvents: false, - supportBonjour: false, - manfDescription: "Night Mode service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.armWithType(value, "night"); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Turn on the Night alarm", - designedMaxLength: 1 - }] - },{ - sType: types.SWITCH_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Stay Mode", - supportEvents: false, - supportBonjour: false, - manfDescription: "Stay Mode service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { that.armWithType(value, "stay"); }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Turn on the Stay alarm", - designedMaxLength: 1 - }] - }]; - } -}; - -// Enable cookie handling and append our expected headers -request = request.defaults({ - headers: { - "X-appkey": "comcastTokenKey", - "X-ClientInfo": "5.2.51", - "X-format": "json" - } -}); - -module.exports.accessory = XfinityHomeAccessory; diff --git a/app.js b/app.js deleted file mode 100644 index 7ed29d1..0000000 --- a/app.js +++ /dev/null @@ -1,189 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var storage = require('node-persist'); -var crypto = require('crypto'); - -console.log("Starting HomeBridge server..."); - -// Look for the configuration file -var configPath = path.join(__dirname, "config.json"); - -// Complain and exit if it doesn't exist yet -if (!fs.existsSync(configPath)) { - console.log("Couldn't find a config.json file in the same directory as app.js. Look at config-sample.json for examples of how to format your config.js and add your home accessories."); - process.exit(1); -} - -// Initialize persistent storage -storage.initSync(); - -// Load up the configuration file -var config = JSON.parse(fs.readFileSync(configPath)); - -// Just to prevent them getting garbage collected -var accessories = []; - -function startup() { - if (config.platforms) loadPlatforms(); - if (config.accessories) loadAccessories(); -} - -function loadAccessories() { - - // Instantiate all accessories in the config - console.log("Loading " + config.accessories.length + " accessories..."); - for (var i=0; i", - "username": "your-liftmaster-username", - "password" : "your-liftmaster-password" - }, - { - "accessory": "Sonos", - "name": "Speakers", - "description": "This shim supports Sonos devices on the same network as this server. It acts as a simple switch that calls play() or pause() on the Sonos, so it's only useful for pausing and resuming tracks or radio stations that are already in the queue. When 'play_volume' is nonzero, the volume will be reset to that value when it turns the Sonos on.", - "play_volume": 25 - }, - { - "accessory": "Lockitron", - "name": "Front Door", - "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.", - "lock_id": "your-lock-id", - "api_token" : "your-lockitron-api-access-token" - }, - { - "accessory": "Carwings", - "name": "Leaf", - "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.", - "username": "your-carwings-username", - "password" : "your-carwings-password" - }, - { - "accessory": "XfinityHome", - "name": "Xfinity Home", - "description": "This shim supports the 'Xfinity Home' security system. Unfortunately I don't know how to generate the 'dsig' property, so you'll need to figure yours out by running the Xfinity Home app on your iOS device while connected to a proxy server like Charles. If you didn't understand any of that, sorry! I welcome any suggestions for how to figure out dsig automatically.", - "email": "your-comcast-email@example.com", - "password": "your-comcast-password", - "dsig": "your-digital-signature", - "pin": "your-security-system-pin-code" - }, - { - "accessory": "HomeMatic", - "name": "Light", - "description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)", - "ccu_id": "The XMP-API id of your HomeMatic device", - "ccu_ip": "The IP-Adress of your HomeMatic CCU device" - }, - { - "accessory": "X10", - "name": "Lamp", - "ip_address": "localhost:3000", - "device_id": "E1", - "protocol": "pl", - "can_dim": true - }, - { - "accessory": "Http", - "name": "Kitchen Lamp", - "on_url": "https://192.168.1.22:3030/devices/23222/on", - "off_url": "https://192.168.1.22:3030/devices/23222/off", - "brightness_url": "https://192.168.1.22:3030/devices/23222/brightness/%b", - "http_method": "POST" - },{ - "accessory": "ELKM1", - "name": "Security System", - "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.", - "zone": "1", - "host": "192.168.1.10", - "port": "2101", - "pin": "1234", - "arm": "Away" - }, - { - "accessory": "AD2USB", - "name": "Alarm", - "description": "Arm, disarm, and status monitoring of the default partition for Honeywell/Ademco alarm systems. Requires network configured AD2USB interface", - "host": "192.168.1.200", // IP address of the SER2SOCK service - "port" : 4999, // Port the SER2SOCK process is running on - "pin": "1234" // PIN used for arming / disarming - } - ] -} diff --git a/lib/homebridge.js b/lib/homebridge.js new file mode 100644 index 0000000..16e537b --- /dev/null +++ b/lib/homebridge.js @@ -0,0 +1,14 @@ +import fs from 'fs'; +import cli from './homebridge/cli'; + +// +// Main HomeBridge Module with global exports. +// + +// HomeBridge version +export const HOMEBRIDGE_VERSION = JSON.parse(fs.readFileSync('package.json')).version; + +// HomeBridge CLI +export { cli } + +// HomeBridge API diff --git a/lib/homebridge/cli.js b/lib/homebridge/cli.js new file mode 100644 index 0000000..929097a --- /dev/null +++ b/lib/homebridge/cli.js @@ -0,0 +1,24 @@ +import Server from './server'; +import program from 'commander'; +import { HOMEBRIDGE_VERSION } from '../homebridge'; + +export default function() { + + // Global options (none currently) and version printout + program + .version(HOMEBRIDGE_VERSION); + + // Run the HomeBridge server + program + .command('server') + .description('Run the HomeBridge server') + .action((options) => new Server(options).run()); + + // Parse options and execute HomeBridge + program.parse(process.argv); + + // Display help by default if no commands or options given + if (!process.argv.slice(2).length) { + program.outputHelp(); + } +} diff --git a/lib/homebridge/provider.js b/lib/homebridge/provider.js new file mode 100644 index 0000000..371f939 --- /dev/null +++ b/lib/homebridge/provider.js @@ -0,0 +1,89 @@ +import path from 'path'; +import fs from 'fs'; +import semver from 'semver'; +import { HOMEBRIDGE_VERSION } from '../homebridge'; + +// This class represents a HomeBridge Provider that may or may not be installed. +export class Provider { + + constructor(name) { + this.name = name; + } + + get path():string { + return path.join(Provider.installedProvidersDir, this.name); + } + + load():object { + + // check for a package.json + let pjsonPath:string = path.join(this.path, "package.json"); + let pjson:object = null; + + if (!fs.existsSync(pjsonPath)) { + throw new Error(`Provider ${this.name} does not contain a package.json.`); + } + + try { + // attempt to parse package.json + pjson = JSON.parse(fs.readFileSync(pjsonPath)); + } + catch (err) { + throw new Error(`Provider ${this.name} contains an invalid package.json. Error: ${err}`); + } + + // pluck out the HomeBridge version requirement + if (!pjson.engines || !pjson.engines.homebridge) { + throw new Error(`Provider ${this.name} does not contain a valid HomeBridge version requirement.`); + } + + let versionRequired:string = pjson.engines.homebridge; + + // make sure the version is satisfied by the currently running version of HomeBridge + if (!semver.satisfies(HOMEBRIDGE_VERSION, versionRequired)) { + throw new Error(`Provider ${this.name} requires a HomeBridge version of "${versionRequired}" which does not satisfy the current HomeBridge version of ${HOMEBRIDGE_VERSION}. You may need to upgrade your installation of HomeBridge.`); + } + + // figure out the main module - index.js unless otherwise specified + let main:string = pjson.main || "./index.js"; + + let mainPath:string = path.join(this.path, main); + + // try to require() it + let loadedProvider:object = require(mainPath); + + return loadedProvider; + } + + // Gets all providers installed on the local system + static installed():Array { + + let providers:Array = []; + let names = fs.readdirSync(Provider.installedProvidersDir); + + for (let name of names) { + + // reconstruct full path + let fullPath:string = path.join(Provider.installedProvidersDir, name); + + // we only care about directories + if (!fs.statSync(fullPath).isDirectory()) continue; + + providers.push(new Provider(name)); + } + + return providers; + } + + // + // Private utility functions + // + + static get userDir():string { + return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; + } + + static get installedProvidersDir():string { + return path.join(Provider.userDir, ".homebridge/providers/node_modules"); + } +} diff --git a/lib/homebridge/server.js b/lib/homebridge/server.js new file mode 100644 index 0000000..def008e --- /dev/null +++ b/lib/homebridge/server.js @@ -0,0 +1,14 @@ +import { Provider } from './provider'; + +export default class Server { + + run() { + // get all installed providers + let providers:Array = Provider.installed(); + + // validate providers - check for valid package.json, etc. + providers.forEach((provider) => provider.load()); + + console.log(`Loaded ${providers.length} providers.`); + } +} diff --git a/package.json b/package.json index 9f7efaa..cd141ae 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,34 @@ { "name": "homebridge", - "description": "HomeKit support for the impatient", + "description": "HomeKit support for existing home devices, today.", "version": "0.0.0", - "scripts": { - "start": "node app.js" + "author": { + "name": "Nick Farina" }, "repository": { "type": "git", "url": "git://github.com/nfarina/homebridge.git" }, - "license": "ISC", + "bugs": { + "url": "http://github.com/nfarina/homebridge/issues" + }, + "licenses": [ + { + "type": "ISC", + "url": "http://github.com/nfarina/homebridge/blob/master/LICENSE" + } + ], + "bin": { + "homebridge": "bin/homebridge" + }, + "engines": { + "node": ">= 0.12.0" + }, + "preferGlobal": true, "dependencies": { + "babel": "^5.6.14", + "commander": "^2.8.1", "hap-nodejs": "git+https://github.com/khaost/HAP-NodeJS#2a1bc8d99a2009317ab5da93faebea34c89f197c", - "ad2usb": "git+https://github.com/alistairg/node-ad2usb.git#local", - "request": "2.49.x", - "node-persist": "0.0.x", - "xmldoc": "0.1.x", - "node-hue-api": "^1.0.5", - "xml2js": "0.4.x", - "carwingsjs": "0.0.x", - "sonos": "0.8.x", - "wemo": "0.2.x", - "wink-js": "0.0.5", - "elkington": "kevinohara80/elkington" + "semver": "^4.3.6" } } diff --git a/platforms/Domoticz.js b/platforms/Domoticz.js deleted file mode 100644 index aacce99..0000000 --- a/platforms/Domoticz.js +++ /dev/null @@ -1,320 +0,0 @@ -// Domoticz Platform Shim for HomeBridge -// Written by Joep Verhaeg (http://www.joepverhaeg.nl) -// -// Revisions: -// -// 12 June 2015 [GizMoCuz] -// - Added support for RGB lights -// - Added support for Scenes -// - Sorting device names -// -// Domoticz JSON API required -// https://www.domoticz.com/wiki/Domoticz_API/JSON_URL's#Lights_and_switches -// -// Remember to add platform to config.json. Example: -// "platforms": [ -// { -// "platform": "Domoticz", -// "name": "Domoticz", -// "server": "127.0.0.1", -// "port": "8080", -// "roomid": 123 (0=no roomplan) -// } -// ], -// -// When you attempt to add a device, it will ask for a "PIN code". -// The default code for all HomeBridge accessories is 031-45-154. -// -var types = require("HAP-NodeJS/accessories/types.js"); -var request = require("request"); - -function DomoticzPlatform(log, config){ - this.log = log; - this.server = config["server"]; - this.port = config["port"]; - this.roomid = 0; - if (typeof config["roomid"] != 'undefined') { - this.roomid = config["roomid"]; - } -} - -function sortByKey(array, key) { - return array.sort(function(a, b) { - var x = a[key]; var y = b[key]; - return ((x < y) ? -1 : ((x > y) ? 1 : 0)); - }); -} - -DomoticzPlatform.prototype = { - accessories: function(callback) { - this.log("Fetching Domoticz lights and switches..."); - - var that = this; - var foundAccessories = []; - if (this.roomid == 0) { - //Get Lights - request.get({ - url: "http://" + this.server + ":" + this.port + "/json.htm?type=devices&filter=light&used=true&order=Name", - json: true - }, function(err, response, json) { - if (!err && response.statusCode == 200) { - if (json['result'] != undefined) { - var sArray=sortByKey(json['result'],"Name"); - sArray.map(function(s) { - accessory = new DomoticzAccessory(that.log, that.server, that.port, false, s.idx, s.Name, s.HaveDimmer, s.MaxDimLevel, (s.SubType=="RGB")||(s.SubType=="RGBW")); - foundAccessories.push(accessory); - }) - } - callback(foundAccessories); - } else { - that.log("There was a problem connecting to Domoticz."); - } - }); - } - else { - //Get all devices specified in the room - request.get({ - url: "http://" + this.server + ":" + this.port + "/json.htm?type=devices&plan=" + this.roomid, - json: true - }, function(err, response, json) { - if (!err && response.statusCode == 200) { - if (json['result'] != undefined) { - var sArray=sortByKey(json['result'],"Name"); - sArray.map(function(s) { - //only accept switches for now - if (typeof s.SwitchType != 'undefined') { - accessory = new DomoticzAccessory(that.log, that.server, that.port, false, s.idx, s.Name, s.HaveDimmer, s.MaxDimLevel, (s.SubType=="RGB")||(s.SubType=="RGBW")); - foundAccessories.push(accessory); - } - }) - } - callback(foundAccessories); - } else { - that.log("There was a problem connecting to Domoticz."); - } - }); - } - //Get Scenes - foundAccessories = []; - request.get({ - url: "http://" + this.server + ":" + this.port + "/json.htm?type=scenes", - json: true - }, function(err, response, json) { - if (!err && response.statusCode == 200) { - if (json['result'] != undefined) { - var sArray=sortByKey(json['result'],"Name"); - sArray.map(function(s) { - accessory = new DomoticzAccessory(that.log, that.server, that.port, true, s.idx, s.Name, false, 0, false); - foundAccessories.push(accessory); - }) - } - callback(foundAccessories); - } else { - that.log("There was a problem connecting to Domoticz."); - } - }); - } -} - -function DomoticzAccessory(log, server, port, IsScene, idx, name, HaveDimmer, MaxDimLevel, HaveRGB) { - // device info - this.IsScene = IsScene; - this.idx = idx; - this.name = name; - this.HaveDimmer = HaveDimmer; - this.MaxDimLevel = MaxDimLevel; - this.HaveRGB = HaveRGB; - this.log = log; - this.server = server; - this.port = port; -} - -DomoticzAccessory.prototype = { - command: function(c,value) { - this.log(this.name + " sending command " + c + " with value " + value); - if (this.IsScene == false) { - //Lights - if (c == "On" || c == "Off") { - url = "http://" + this.server + ":" + this.port + "/json.htm?type=command¶m=switchlight&idx=" + this.idx + "&switchcmd=" + c + "&level=0"; - } - else if (c == "setHue") { - url = "http://" + this.server + ":" + this.port + "/json.htm?type=command¶m=setcolbrightnessvalue&idx=" + this.idx + "&hue=" + value + "&brightness=100" + "&iswhite=false"; - } - else if (c == "setLevel") { - url = "http://" + this.server + ":" + this.port + "/json.htm?type=command¶m=switchlight&idx=" + this.idx + "&switchcmd=Set%20Level&level=" + value; - } - else if (value != undefined) { - this.log(this.name + " Unhandled Light command! cmd=" + c + ", value=" + value); - } - } - else { - //Scenes - if (c == "On" || c == "Off") { - url = "http://" + this.server + ":" + this.port + "/json.htm?type=command¶m=switchscene&idx=" + this.idx + "&switchcmd=" + c; - } - else if (value != undefined) { - this.log(this.name + " Unhandled Scene command! cmd=" + c + ", value=" + value); - } - } - - var that = this; - request.put({ url: url }, function(err, response) { - if (err) { - that.log("There was a problem sending command " + c + " to" + that.name); - that.log(url); - } else { - that.log(that.name + " sent command " + c); - } - }) - }, - - informationCharacteristics: function() { - return [ - { - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Domoticz", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - } - ] - }, - - controlCharacteristics: function(that) { - cTypes = [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - }] - - if (this.idx != undefined) { - cTypes.push({ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { - if (value == 0) { - that.command("Off") - } else { - that.command("On") - } - }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1 - }) - } - - if (this.HaveDimmer == true) { - cTypes.push({ - cType: types.BRIGHTNESS_CTYPE, - onUpdate: function(value) { that.command("setLevel", value); }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Adjust Brightness of Light", - designedMinValue: 0, - designedMaxValue: this.MaxDimLevel, - designedMinStep: 1, - unit: "%" - }) - } - if (this.HaveRGB == true) { - cTypes.push({ - cType: types.HUE_CTYPE, - onUpdate: function(value) { that.command("setHue", value); }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Adjust Hue of Light", - designedMinValue: 0, - designedMaxValue: 360, - designedMinStep: 1, - unit: "arcdegrees" - }) - } - - return cTypes - }, - - sType: function() { - if (this.HaveDimmer == true) { - return types.LIGHTBULB_STYPE - } else { - return types.SWITCH_STYPE - } - }, - - getServices: function() { - var that = this; - var services = [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: this.informationCharacteristics(), - }, - { - sType: this.sType(), - characteristics: this.controlCharacteristics(that) - }]; - this.log("Loaded services for " + this.name) - return services; - } -}; - -module.exports.accessory = DomoticzAccessory; -module.exports.platform = DomoticzPlatform; \ No newline at end of file diff --git a/platforms/ISY.js b/platforms/ISY.js deleted file mode 100644 index b4b20a8..0000000 --- a/platforms/ISY.js +++ /dev/null @@ -1,385 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var xml2js = require('xml2js'); -var request = require('request'); -var util = require('util'); - -var parser = new xml2js.Parser(); - - -var power_state_ctype = { - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { return; }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1 -}; - -function ISYURL(user, pass, host, port, path) { - return util.format("http://%s:%s@%s:%d%s", user, pass, host, port, encodeURI(path)); -} - -function ISYPlatform(log, config) { - this.host = config["host"]; - this.port = config["port"]; - this.user = config["username"]; - this.pass = config["password"]; - - this.log = log; -} - -ISYPlatform.prototype = { - accessories: function(callback) { - this.log("Fetching ISY Devices."); - - var that = this; - var url = ISYURL(this.user, this.pass, this.host, this.port, "/rest/nodes"); - - var options = { - url: url, - method: 'GET' - }; - - var foundAccessories = []; - - request(options, function(error, response, body) { - if (error) - { - console.trace("Requesting ISY devices."); - that.log(error); - return error; - } - - parser.parseString(body, function(err, result) { - result.nodes.node.forEach(function(obj) { - var enabled = obj.enabled[0] == 'true'; - - if (enabled) - { - var device = new ISYAccessory( - that.log, - that.host, - that.port, - that.user, - that.pass, - obj.name[0], - obj.address[0], - obj.property[0].$.uom - ); - - foundAccessories.push(device); - } - }); - }); - - callback(foundAccessories.sort(function (a,b) { - return (a.name > b.name) - (a.name < b.name); - })); - }); - } -} - -function ISYAccessory(log, host, port, user, pass, name, address, uom) { - this.log = log; - this.host = host; - this.port = port; - this.user = user; - this.pass = pass; - this.name = name; - this.address = address; - this.uom = uom; -} - -ISYAccessory.prototype = { - query: function() { - var path = util.format("/rest/status/%s", encodeURI(this.address)); - var url = ISYURL(this.user, this.pass, this.host, this.port, path); - - var options = { url: url, method: 'GET' }; - request(options, function(error, response, body) { - if (error) - { - console.trace("Requesting Device Status."); - that.log(error); - return error; - } - - parser.parseString(body, function(err, result) { - var value = result.properties.property[0].$.value; - return value; - }); - - }); - }, - - command: function(c, value) { - this.log(this.name + " sending command " + c + " with value " + value); - - switch (c) - { - case 'On': - path = "/rest/nodes/" + this.address + "/cmd/DFON"; - break; - case 'Off': - path = "/rest/nodes/" + this.address + "/cmd/DFOF"; - break; - case 'Low': - path = "/rest/nodes/" + this.address + "/cmd/DON/85"; - break; - case 'Medium': - path = "/rest/nodes/" + this.address + "/cmd/DON/128"; - break; - case 'High': - path = "/rest/nodes/" + this.address + "/cmd/DON/255"; - break; - case 'setLevel': - if (value > 0) - { - path = "/rest/nodes/" + this.address + "/cmd/DON/" + Math.floor(255 * (value / 100)); - } - break; - default: - this.log("Unimplemented command sent to " + this.name + " Command " + c); - break; - } - - if (path) - { - var url = ISYURL(this.user, this.pass, this.host, this.port, path); - var options = { - url: url, - method: 'GET' - }; - - var that = this; - request(options, function(error, response, body) { - if (error) - { - console.trace("Sending Command."); - that.log(error); - return error; - } - that.log("Sent command " + path + " to " + that.name); - }); - } - }, - - informationCharacteristics: function() { - return [ - { - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "SmartHome", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.address, - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - } - ] - }, - - controlCharacteristics: function(that) { - cTypes = [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - }] - - if (this.uom == "%/on/off") { - cTypes.push({ - cType: types.POWER_STATE_CTYPE, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1, - onUpdate: function(value) { - if (value == 0) { - that.command("Off") - } else { - that.command("On") - } - }, - onRead: function() { - return this.query(); - } - }); - cTypes.push({ - cType: types.BRIGHTNESS_CTYPE, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Adjust Brightness of Light", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%", - onUpdate: function(value) { - that.command("setLevel", value); - }, - onRead: function() { - var val = this.query(); - that.log("Query: " + val); - return val; - } - }); - } - else if (this.uom == "off/low/med/high") - { - cTypes.push({ - cType: types.POWER_STATE_CTYPE, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1, - onUpdate: function(value) { - if (value == 0) { - that.command("Off") - } else { - that.command("On") - } - }, - onRead: function() { - return this.query(); - } - }); - cTypes.push({ - cType: types.ROTATION_SPEED_CTYPE, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the speed of the fan", - designedMaxLength: 1, - onUpdate: function(value) { - if (value == 0) { - that.command("Off"); - } else if (value > 0 && value < 40) { - that.command("Low"); - } else if (value > 40 && value < 75) { - that.command("Medium"); - } else { - that.command("High"); - } - }, - onRead: function() { - return this.query(); - } - }); - } - else if (this.uom == "on/off") - { - cTypes.push({ - cType: types.POWER_STATE_CTYPE, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1, - onUpdate: function(value) { - if (value == 0) { - that.command("Off") - } else { - that.command("On") - } - }, - onRead: function() { - return this.query(); - } - }); - } - - return cTypes; - }, - - sType: function() { - if (this.uom == "%/on/off") { - return types.LIGHTBULB_STYPE; - } else if (this.uom == "on/off") { - return types.SWITCH_STYPE; - } else if (this.uom == "off/low/med/high") { - return types.FAN_STYPE; - } - - return types.SWITCH_STYPE; - }, - - getServices: function() { - var that = this; - var services = [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: this.informationCharacteristics(), - }, - { - sType: this.sType(), - characteristics: this.controlCharacteristics(that) - }]; - - //that.log("Loaded services for " + that.name); - return services; - } -}; - -module.exports.accessory = ISYAccessory; -module.exports.platform = ISYPlatform; diff --git a/platforms/PhilipsHue.js b/platforms/PhilipsHue.js deleted file mode 100644 index 0988b06..0000000 --- a/platforms/PhilipsHue.js +++ /dev/null @@ -1,369 +0,0 @@ -// Philips Hue Platform Shim for HomeBridge -// -// Remember to add platform to config.json. Example: -// "platforms": [ -// { -// "platform": "PhilipsHue", -// "name": "Philips Hue", -// "ip_address": "127.0.0.1", -// "username": "252deadbeef0bf3f34c7ecb810e832f" -// } -// ], -// -// If you do not know the IP address of your Hue Bridge, simply leave it blank and your Bridge -// will be discovered automatically. -// -// If you do not have a "username" for your Hue API already, simply leave the field blank and -// you will be prompted to press the link button on your Hue Bridge before running HomeBridge. -// A username will be created for you and printed out, then the server will exit so you may -// enter it in your config.json. -// -// -// When you attempt to add a device, it will ask for a "PIN code". -// The default code for all HomeBridge accessories is 031-45-154. -// - -/* jslint node: true */ -/* globals require: false */ -/* globals config: false */ - -"use strict"; - -var hue = require("node-hue-api"), - HueApi = hue.HueApi, - lightState = hue.lightState; - -var types = require("HAP-NodeJS/accessories/types.js"); - -function PhilipsHuePlatform(log, config) { - this.log = log; - this.ip_address = config["ip_address"]; - this.username = config["username"]; -} - -function PhilipsHueAccessory(log, device, api) { - this.id = device.id; - this.name = device.name; - this.model = device.modelid; - this.device = device; - this.api = api; - this.log = log; -} - -// Get the ip address of the first available bridge with meethue.com or a network scan. -var locateBridge = function (callback) { - var that = this; - - // Report the results of the scan to the user - var getIp = function (err, bridges) { - if (!bridges || bridges.length === 0) { - that.log("No Philips Hue bridges found."); - callback(err || new Error("No bridges found")); - return; - } - - if (bridges.length > 1) { - that.log("Warning: Multiple Philips Hue bridges detected. The first bridge will be used automatically. To use a different bridge, set the `ip_address` manually in the configuration."); - } - - that.log( - "Philips Hue bridges found:\n" + - (bridges.map(function (bridge) { - // Bridge name is only returned from meethue.com so use id instead if it isn't there - return "\t" + bridge.ipaddress + ' - ' + (bridge.name || bridge.id); - })).join("\n") - ); - - callback(null, bridges[0].ipaddress); - }; - - // Try to discover the bridge ip using meethue.com - that.log("Attempting to discover Philips Hue bridge with meethue.com..."); - hue.nupnpSearch(function (locateError, bridges) { - if (locateError) { - that.log("Philips Hue bridge discovery with meethue.com failed. Register your bridge with the meethue.com for more reliable discovery."); - - that.log("Attempting to discover Philips Hue bridge with network scan..."); - - // Timeout after one minute - hue.upnpSearch(60000) - .then(function (bridges) { - that.log("Scan complete"); - getIp(null, bridges); - }) - .fail(function (scanError) { - that.log("Philips Hue bridge discovery with network scan failed. Check your network connection or set ip_address manually in configuration."); - getIp(new Error("Scan failed: " + scanError.message)); - }).done(); - } else { - getIp(null, bridges); - } - }); -}; - -PhilipsHuePlatform.prototype = { - - accessories: function(callback) { - this.log("Fetching Philips Hue lights..."); - var that = this; - var getLights = function () { - var api = new HueApi(that.ip_address, that.username); - - // Connect to the API - // Get a dump of all lights, so as not to hit rate limiting for installations with larger amounts of bulbs - - api.fullState(function(err, response) { - if (err) throw err; - - var foundAccessories = []; - for (var deviceId in response.lights) { - var device = response.lights[deviceId]; - device.id = deviceId; - var accessory = new PhilipsHueAccessory(that.log, device, api); - foundAccessories.push(accessory); - } - callback(foundAccessories); - - }); - }; - - // Create a new user if needed - function checkUsername() { - if (!that.username) { - var api = new HueApi(that.ip_address); - api.createUser(that.ip_address, null, null, function(err, user) { - - // try and help explain this particular error - if (err && err.message == "link button not pressed") - throw "Please press the link button on your Philips Hue bridge, then start the HomeBridge server within 30 seconds."; - - if (err) throw err; - - throw "Created a new username " + JSON.stringify(user) + " for your Philips Hue. Please add it to your config.json then start the HomeBridge server again: "; - }); - } - else { - getLights(); - } - } - - // Discover the bridge if needed - if (!this.ip_address) { - locateBridge.call(this, function (err, ip_address) { - if (err) throw err; - - // TODO: Find a way to persist this - that.ip_address = ip_address; - that.log("Save the Philips Hue bridge ip address "+ ip_address +" to your config to skip discovery."); - checkUsername(); - }); - } else { - checkUsername(); - } - } -}; - -PhilipsHueAccessory.prototype = { - // Convert 0-65535 to 0-360 - hueToArcDegrees: function(value) { - value = value/65535; - value = value*100; - value = Math.round(value); - return value; - }, - // Convert 0-360 to 0-65535 - arcDegreesToHue: function(value) { - value = value/360; - value = value*65535; - value = Math.round(value); - return value; - }, - // Convert 0-255 to 0-100 - bitsToPercentage: function(value) { - value = value/255; - value = value*100; - value = Math.round(value); - return value; - }, - // Create and set a light state - executeChange: function(api, device, characteristic, value) { - var that = this; - var state = lightState.create(); - switch(characteristic.toLowerCase()) { - case 'identify': - state.alert('select'); - break; - case 'power': - if (value) { - state.on(); - } - else { - state.off(); - } - break; - case 'hue': - state.hue(this.arcDegreesToHue(value)); - break; - case 'brightness': - state.brightness(value); - break; - case 'saturation': - state.saturation(value); - break; - } - api.setLightState(device.id, state, function(err, lights) { - if (!err) { - that.log(device.name + ", characteristic: " + characteristic + ", value: " + value + "."); - } - else { - that.log(err); - } - }); - }, - // Get Services - getServices: function() { - var that = this; - var bulb_characteristics = [ - { - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { - that.executeChange(that.api, that.device, "power", value); - }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: that.device.state.on, - supportEvents: false, - supportBonjour: false, - manfDescription: "Turn On the Light", - designedMaxLength: 1 - },{ - cType: types.BRIGHTNESS_CTYPE, - onUpdate: function(value) { - that.executeChange(that.api, that.device, "brightness", value); - }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: that.bitsToPercentage(that.device.state.bri), - supportEvents: false, - supportBonjour: false, - manfDescription: "Adjust Brightness of Light", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - } - ]; - // Handle the Hue/Hue Lux divergence - if (that.device.state.hasOwnProperty('hue') && that.device.state.hasOwnProperty('sat')) { - bulb_characteristics.push({ - cType: types.HUE_CTYPE, - onUpdate: function(value) { - that.executeChange(that.api, that.device, "hue", value); - }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: that.hueToArcDegrees(that.device.state.hue), - supportEvents: false, - supportBonjour: false, - manfDescription: "Adjust Hue of Light", - designedMinValue: 0, - designedMaxValue: 360, - designedMinStep: 1, - unit: "arcdegrees" - }); - bulb_characteristics.push({ - cType: types.SATURATION_CTYPE, - onUpdate: function(value) { - that.executeChange(that.api, that.device, "saturation", value); - }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: that.bitsToPercentage(that.device.state.sat), - supportEvents: false, - supportBonjour: false, - manfDescription: "Adjust Saturation of Light", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }); - } - var accessory_data = [ - { - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [ - { - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Philips", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: that.model, - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: that.device.uniqueid, - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: function(value) { - that.executeChange(that.api, that.device, "identify", value); - }, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - } - ] - },{ - sType: types.LIGHTBULB_STYPE, - // `bulb_characteristics` defined based on bulb type - characteristics: bulb_characteristics - } - ]; - return accessory_data; - } -}; - -module.exports.platform = PhilipsHuePlatform; diff --git a/platforms/SmartThings.js b/platforms/SmartThings.js deleted file mode 100644 index 580e70b..0000000 --- a/platforms/SmartThings.js +++ /dev/null @@ -1,242 +0,0 @@ -// SmartThings JSON API SmartApp required -// https://github.com/jnewland/SmartThings/blob/master/JSON.groovy -// -var types = require("HAP-NodeJS/accessories/types.js"); -var request = require("request"); - -function SmartThingsPlatform(log, config){ - this.log = log; - this.app_id = config["app_id"]; - this.access_token = config["access_token"]; -} - -SmartThingsPlatform.prototype = { - accessories: function(callback) { - this.log("Fetching SmartThings devices..."); - - var that = this; - var foundAccessories = []; - - request.get({ - url: "https://graph.api.smartthings.com/api/smartapps/installations/"+this.app_id+"/devices?access_token="+this.access_token, - json: true - }, function(err, response, json) { - if (!err && response.statusCode == 200) { - if (json['switches'] != undefined) { - json['switches'].map(function(s) { - accessory = new SmartThingsAccessory(that.log, s.name, s.commands); - foundAccessories.push(accessory); - }) - } - if (json['hues'] != undefined) { - json['hues'].map(function(s) { - accessory = new SmartThingsAccessory(that.log, s.name, s.commands); - foundAccessories.push(accessory); - }) - } - callback(foundAccessories); - } else { - that.log("There was a problem authenticating with SmartThings."); - } - }); - - } -} - -function SmartThingsAccessory(log, name, commands) { - // device info - this.name = name; - this.commands = commands; - this.log = log; -} - -SmartThingsAccessory.prototype = { - - command: function(c,value) { - this.log(this.name + " sending command " + c); - var url = this.commands[c]; - if (value != undefined) { - url = this.commands[c] + "&value="+value - } - - var that = this; - request.put({ - url: url - }, function(err, response) { - if (err) { - that.log("There was a problem sending command " + c + " to" + that.name); - that.log(url); - } else { - that.log(that.name + " sent command " + c); - } - }) - }, - - informationCharacteristics: function() { - return [ - { - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: false, - supportBonjour: false, - manfDescription: "Name of the accessory", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "SmartThings", - supportEvents: false, - supportBonjour: false, - manfDescription: "Manufacturer", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Model", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "SN", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - } - ] - }, - - controlCharacteristics: function(that) { - cTypes = [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: this.name, - supportEvents: true, - supportBonjour: false, - manfDescription: "Name of service", - designedMaxLength: 255 - }] - - if (this.commands['on'] != undefined) { - cTypes.push({ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) { - if (value == 0) { - that.command("off") - } else { - that.command("on") - } - }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Change the power state", - designedMaxLength: 1 - }) - } - - if (this.commands['on'] != undefined) { - cTypes.push({ - cType: types.BRIGHTNESS_CTYPE, - onUpdate: function(value) { that.command("setLevel", value); }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Adjust Brightness of Light", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }) - } - - if (this.commands['setHue'] != undefined) { - cTypes.push({ - cType: types.HUE_CTYPE, - onUpdate: function(value) { that.command("setHue", value); }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Adjust Hue of Light", - designedMinValue: 0, - designedMaxValue: 360, - designedMinStep: 1, - unit: "arcdegrees" - }) - } - - if (this.commands['setSaturation'] != undefined) { - cTypes.push({ - cType: types.SATURATION_CTYPE, - onUpdate: function(value) { that.command("setSaturation", value); }, - perms: ["pw","pr","ev"], - format: "int", - initialValue: 0, - supportEvents: true, - supportBonjour: false, - manfDescription: "Adjust Brightness of Light", - designedMinValue: 0, - designedMaxValue: 100, - designedMinStep: 1, - unit: "%" - }) - } - - return cTypes - }, - - sType: function() { - if (this.commands['setLevel'] != undefined) { - return types.LIGHTBULB_STYPE - } else { - return types.SWITCH_STYPE - } - }, - - getServices: function() { - var that = this; - var services = [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: this.informationCharacteristics(), - }, - { - sType: this.sType(), - characteristics: this.controlCharacteristics(that) - }]; - this.log("Loaded services for " + this.name) - return services; - } -}; - -module.exports.accessory = SmartThingsAccessory; -module.exports.platform = SmartThingsPlatform; diff --git a/platforms/Wink.js b/platforms/Wink.js deleted file mode 100644 index e2ec455..0000000 --- a/platforms/Wink.js +++ /dev/null @@ -1,252 +0,0 @@ -var types = require("HAP-NodeJS/accessories/types.js"); -var wink = require('wink-js'); - -var model = { - light_bulbs: require('wink-js/lib/model/light') -}; - - -function WinkPlatform(log, config){ - - // auth info - this.client_id = config["client_id"]; - this.client_secret = config["client_secret"]; - this.username = config["username"]; - this.password = config["password"]; - - this.log = log; -} - -WinkPlatform.prototype = { - accessories: function(callback) { - this.log("Fetching Wink devices."); - - var that = this; - var foundAccessories = []; - - wink.init({ - "client_id": this.client_id, - "client_secret": this.client_secret, - "username": this.username, - "password": this.password - }, function(auth_return) { - if ( auth_return === undefined ) { - that.log("There was a problem authenticating with Wink."); - } else { - // success - wink.user().devices('light_bulbs', function(devices) { - for (var i=0; i