From c73ee5699355ca030fb39c81ea3a8274ecdedf21 Mon Sep 17 00:00:00 2001 From: Foxi352 Date: Fri, 1 Jul 2016 22:13:37 +0200 Subject: [PATCH] First commit of files --- SmartHomeNGConnection.js | 83 +++++++++++++++ index.js | 212 +++++++++++++++++++++++++++++++++++++++ package.json | 20 ++++ 3 files changed, 315 insertions(+) create mode 100755 SmartHomeNGConnection.js create mode 100755 index.js create mode 100644 package.json diff --git a/SmartHomeNGConnection.js b/SmartHomeNGConnection.js new file mode 100755 index 0000000..916f509 --- /dev/null +++ b/SmartHomeNGConnection.js @@ -0,0 +1,83 @@ +var http = require("http"); +var WebSocketClient = require('websocket').client + + +function SmartHomeNGConnection(platform, log, host, port) { + this.log = log; + this.platform = platform; + this.connected = false; + this.updateCallback = undefined; + this.tomonitor = []; + + this.shng_host = host; + this.shng_port = port; + +} + +SmartHomeNGConnection.prototype.init = function () { + this.connect(); +} + +SmartHomeNGConnection.prototype.connect = function(host, ip) { + var that = this + this.log("Connecting to SmartHomeNG @ " + this.shng_host); + + this.shng_ws = new WebSocketClient(); + + this.shng_ws.connect('ws://' + this.shng_host + ':' + this.shng_port + '/'); + this.shng_ws.on('connect', function(connection) { + that.log('connected to server!'); + that.connected = true; + that.connection = connection; + that.startMonitoring(); + + connection.on('message', function(message) { that.receive(message); }); + connection.on('error', function(error) { that.log('WebSocket error: ' + error.toString()) }); + connection.on('close', function() { + that.log('Connection to smarthome lost'); + that.connected = false; + }); + + }); + +} + +SmartHomeNGConnection.prototype.receive = function(message) { + var msg = JSON.parse(message.utf8Data); + //this.log(msg); + if (msg.items) { + for (int = 0; int < msg.items.length; int++) { + item = msg.items[int]; + //this.log("Received value " + item[1] + " for item " + item[0]); + if (this.updateCallback) { + this.updateCallback(item[0], item[1]); + } + } + } +} + +SmartHomeNGConnection.prototype.setValue = function(item, value) { + if (this.connected) { + this.connection.send('{"cmd":"item","id":"' + item + '","val":"' + value + '"}') + } else { + this.log("Cannot switch " + item + ", no connection to SmartHomeNG !") + } +} + +SmartHomeNGConnection.prototype.startMonitoring = function() { + if (this.connected && this.tomonitor.length > 0) { + var buffer = {}; + this.log("Start monitoring " + this.tomonitor); + buffer.cmd = 'monitor'; + buffer.items = this.tomonitor; + var command = JSON.stringify(buffer); + this.connection.send(command); + } else { + this.log("Cannot start monitoring, not connected !"); + } +} + +module.exports = { + SmartHomeNGConnection: SmartHomeNGConnection +} + diff --git a/index.js b/index.js new file mode 100755 index 0000000..3c23705 --- /dev/null +++ b/index.js @@ -0,0 +1,212 @@ +// SmartHomeNG Platform Shim for HomeBridge + +var Accessory, Service, Characteristic, UUIDGen; +var fs = require('fs'); +var path = require('path'); +var SmartHomeNGConnection = require("./SmartHomeNGConnection.js").SmartHomeNGConnection; + +module.exports = function(homebridge) { + // Accessory must be created from PlatformAccessory Constructor + Accessory = homebridge.platformAccessory; + + // Service and Characteristic are from hap-nodejs + Service = homebridge.hap.Service; + Characteristic = homebridge.hap.Characteristic; + UUIDGen = homebridge.hap.uuid; + + homebridge.registerPlatform("homebridge-smarthomeng", "SmartHomeNG", SmartHomeNGPlatform); +} + +// Platform constructor +function SmartHomeNGPlatform(log, config, api) { + this.log = log; + this.config = config; + this.accessoriesCache = []; + this.supportedFunctions = ['onoff', 'brightness']; + + if (this.config["host"] != undefined) { + this.shng_host = this.config["host"]; + } else { + this.shng_host = "localhost"; + } + + if (this.config["port"] != undefined) { + this.shng_port = this.config["port"]; + } else { + this.shng_port = 2424; + } + + var that = this + this.log("SmartHomeNG Platform Plugin Version " + this.getVersion()) + + this.shngcon = new SmartHomeNGConnection(this, this.log, this.shng_host, this.shng_port); + this.shngcon.updateCallback = this.update; + + if (api) { + this.api = api; + + // Listen to event "didFinishLaunching", this means homebridge already finished loading cached accessories + // Platform Plugin should only register new accessory that doesn't exist in homebridge after this event. + // Or start discover new accessories + this.api.on('didFinishLaunching', function() { + this.log("Finished loading " + this.accessoriesCache.length + " accessories"); + // Add supported SHNG items to monitoring + var tomonitor = []; + for(i = 0; i < this.accessoriesCache.length; i++) { + var device = this.accessoriesCache[i].device; + for (var key in device) { + if (this.supportedFunctions.indexOf(key) >= 0) { + tomonitor.push(device[key]); + } + } + } + this.shngcon.tomonitor = tomonitor; + }.bind(this)); + } + + this.shngcon.init(); +} + +// Accessory constructor +function SmartHomeNGAccessory(log, device, shngcon) { + this.name = device.name; + this.device = device; + this.log = log; + this.shngcon = shngcon; + this.value = undefined; + /*this.log("CONSTRUCTOR: "); + this.log(device); + this.log("------")*/ +} + +SmartHomeNGPlatform.prototype = { + + // Return our accessories to homebridge + accessories: function(callback) { + var that = this; + var foundAccessories = []; + + this.log("Building list of accessories"); + + var configAccessories = this.config.accessories; + for (var i = 0; i < configAccessories.length; i++) { + this.log("Parsing accessory: '" + configAccessories[i].name + "'."); + + var accessory = new SmartHomeNGAccessory(that.log, configAccessories[i], that.shngcon); + foundAccessories.push(accessory); + } + + //this.log(foundAccessories) + this.accessoriesCounter = foundAccessories.length; + this.accessoriesCache = foundAccessories; + callback(foundAccessories); + }, + + update: function (item, value) { + //this.log("CALLBACK: item " + item + " with value " + value); + for (i = 0; i < this.platform.accessoriesCache.length; i++) { + accessory = this.platform.accessoriesCache[i].device; + // loop through accessories and services to find modified one + for (var key in accessory) { + if (accessory[key] == item) { + this.log("Updating item '" + item + "' with value " + value); + accessory[key + '_value'] = value; + break; + } + } + } + }, + + // Get version info from package.json file + getVersion: function() { + var pjPath = path.join(__dirname, './package.json'); + var pj = JSON.parse(fs.readFileSync(pjPath)); + return pj.version; + } +} + +SmartHomeNGAccessory.prototype = { + + // Enumerate accessory services and characteristics + getServices: function() { + var that = this; + this.log("Setting services for '" + this.name + "'"); + + // check if device type is set in config + if (!this.device.type) { + this.log("Ignoring '" + this.name + "' because no device type found, make sure to have the 'type' parameter in your config.json !"); + return []; + } + + // construct service and characteristics according to device type + switch (this.device.type.toLowerCase()) { + + // Lightbulb service + case 'lightbulb': + var myService = new Service.Lightbulb(this.name); + // On / Off characteristic + if (this.device.onoff) { + this.log("Adding on/off characteristic to " + this.name); + + myService + .getCharacteristic(Characteristic.On) + .on('get', function(callback) { that.getValue("onoff", callback);}) + .on('set', function(value, callback) { that.setValue("onoff", value, callback);}); + } + // Dimmable characteristic + if (this.device.brightness) { + this.log("Adding brightness characteristic to " + this.name); + myService + .addCharacteristic(Characteristic.Brightness) + .on('get', function(callback) { that.getValue("brightness", callback);}) + .on('set', function(value, callback) { that.setValue("brightness", value, callback);}); + } + break; + + // If no supported type is found warn user and return empty services + default: + this.log("Ignoring '" + this.name + "' because device type '" + this.device.type + "' is not supported !"); + return []; + break; + } + + // device information service + var informationService = new Service.AccessoryInformation(); + informationService + .setCharacteristic(Characteristic.Manufacturer, this.manufacturername) + .setCharacteristic(Characteristic.Model, this.model) + .setCharacteristic(Characteristic.SerialNumber, this.device.uniqueid); + + return [informationService, myService]; + }, + + // Get value + getValue: function(characteristic, callback) { + this.log("Get value for " + this.device.name + ", characteristic: " + characteristic + "."); + //this.log(this.device); + if (this.device[characteristic + "_value"] != undefined) { + this.log("Found value '" + this.device[characteristic + "_value"] + "' for '" + characteristic + "' of device '" + this.device.name + "'."); + if (callback) callback(null, this.device[characteristic + "_value"]); + } else { + if (callback) callback("Oh oh :-("); + } + + }, + + // Set value + setValue: function(characteristic, value, callback) { + this.log("Set " + this.device.name + ", characteristic: " + characteristic + ", value: " + value + "."); + // If item for characteristic exists then send value to it + if (this.device[characteristic] != undefined) { + this.shngcon.setValue(this.device[characteristic], value); + } + // Check if callback required + if (callback) callback(); // Success + callback = null; + }, + + // Respond to identify request + identify: function(callback) { + this.log("Identify request for '" + this.device.name + "'."); + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8124191 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "homebridge-smarthomeng", + "version": "1.1.1", + "description": "Platform plugin for SmartHomeNG: https://github.com/smarthomeNG/smarthome", + "license": "GPL", + "keywords": [ + "homebridge-plugin" + ], + "repository": { + "type": "git", + "url": "git://github.com/example/homebridge.git" + }, + "bugs": { + "url": "http://github.com/example/homebridge/issues" + }, + "engines": { + "node": ">=0.12.0", + "homebridge": ">=0.2.0" + } +}