From 681cd7d9eb88cb82dcb1ee223225a61f10db1b41 Mon Sep 17 00:00:00 2001 From: madmod Date: Sat, 11 Jul 2015 19:18:14 -0600 Subject: [PATCH] Add Logitech Harmony platform --- platforms/LogitechHarmony.js | 270 +++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 platforms/LogitechHarmony.js diff --git a/platforms/LogitechHarmony.js b/platforms/LogitechHarmony.js new file mode 100644 index 0000000..494c3ed --- /dev/null +++ b/platforms/LogitechHarmony.js @@ -0,0 +1,270 @@ +'use strict'; + +// Logitech Harmony Remote Platform Shim for HomeBridge +// Based on the Domoticz Platform Shim for HomeBridge by Joep Verhaeg (http://www.joepverhaeg.nl) +// Wriiten by John Wells (https://github.com/madmod) +// +// Remember to add platform to config.json. Example: +// "platforms": [ +// { +// "platform": "LogitechHarmony", +// "name": "Logitech Harmony" +// } +// ], +// +// 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("../lib/HAP-NodeJS/accessories/types.js"); + +var harmonyDiscover = require('harmonyhubjs-discover'); +var harmony = require('harmonyhubjs-client'); + +var _harmonyHubPort = 61991; + + +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)); + }); +}; + + +function LogitechHarmonyPlatform (log, config) { + this.log = log; + this.ip_address = config['ip_address']; +}; + + +LogitechHarmonyPlatform.prototype = { + + // Find one harmony remote hub (only support one for now) + locateHub: function (callback) { + + var that = this; + + // Connect to a Harmony hub + var createClient = function (ipAddress) { + that.log("Connecting to Logitech Harmony remote hub..."); + + harmony(ipAddress) + .then(function (client) { + that.log("Connected to Logitech Harmony remote hub"); + + callback(null, client); + }); + }; + + // Use the ip address in configuration if available + if (this.ip_address) { + console.log("Using Logitech Harmony hub ip address from configuration"); + + return createClient(this.ip_address) + } + + this.log("Searching for Logitech Harmony remote hubs..."); + + // Discover the harmony hub with bonjour + var discover = new harmonyDiscover(_harmonyHubPort); + + // TODO: Support update event with some way to add accessories + // TODO: Have some kind of timeout with an error message. Right now this searches forever until it finds one hub. + discover.on('online', function (hubInfo) { + that.log("Found Logitech Harmony remote hub: " + hubInfo.ip); + + // Stop looking for hubs once we find the first one + // TODO: Support multiple hubs + discover.stop(); + + createClient(hubInfo.ip); + }); + + // Start looking for hubs + discover.start(); + }, + + accessories: function (callback) { + var that = this; + var foundAccessories = []; + + // Get the first hub + this.locateHub(function (err, hub) { + if (err) throw err; + + that.log("Fetching Logitech Harmony devices and activites..."); + + //getDevices(hub); + getActivities(hub); + }); + + // Get Harmony Devices + var getDevices = function(hub) { + that.log("Fetching Logitech Harmony devices..."); + + hub.getDevices() + .then(function (devices) { + that.log("Found devices: ", devices); + + 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); + }); + }; + + // Get Harmony Activities + var getActivities = function(hub) { + that.log("Fetching Logitech Harmony activities..."); + + hub.getActivities() + .then(function (activities) { + that.log("Found activities: \n" + activities.map(function (a) { return "\t" + a.label; }).join("\n")); + + var sArray = sortByKey(activities, "label"); + + sArray.map(function(s) { + var accessory = new LogitechHarmonyAccessory(that.log, hub, s, true); + foundAccessories.push(accessory); + }); + + callback(foundAccessories); + }); + }; + + } + +}; + + +function LogitechHarmonyAccessory (log, hub, details, isActivity) { + this.log = log; + this.hub = hub; + this.details = details; + this.id = details.id; + this.name = details.label; + this.isActivity = isActivity; +}; + + +LogitechHarmonyAccessory.prototype = { + + command: function (command, value) { + this.log(this.name + " sending command " + command + " with value " + value); + if (this.isActivity) { + if (command === "On") { + this.hub.startActivity(this.id) + } else { + this.hub.turnOff(); + } + } else { + // TODO: Support device specific commands + } + }, + + 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: "Logitech", + supportEvents: false, + supportBonjour: false, + manfDescription: "Manufacturer", + designedMaxLength: 255 + },{ + cType: types.MODEL_CTYPE, + onUpdate: null, + perms: ["pr"], + format: "string", + initialValue: "Harmony", + 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: true, + supportBonjour: false, + manfDescription: "Name of service", + designedMaxLength: 255 + }, + { + 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 + } + ] + } + ]; + } +}; + +module.exports.accessory = LogitechHarmonyAccessory; +module.exports.platform = LogitechHarmonyPlatform; +