**edit - Logitech firmware updates have closed the port that this solution used, it no longer works.
See here for an alternate solution that does not need a node middleman
I have something put together that uses a node.js app running on an internal server to listen for status updates from a Harmony hub on the same LAN. I'm not very good with javascript (go easy on me ), but seems to be working ok in my limited testing today. I'm putting this out there in hopes that someone more knowledgeable than me can take this and run with it to try and put together a comprehensive local integration with Hubitat.
Some notes:
- As of right now, this is not a control mechanism for Harmony...it listens for changes that were initiated in other ways and updates the corresponding switches in Hubitat.
- It uses harmonyhubjs-client from swissmanu's github page, I just wrote a small app to use the functionality from harmonyhubjs-client in the way I wanted.
- It does not require any Harmony credentials from the end user. harmonyhubjs-client uses some sort of guest authentication to open a websocket/xmpp connection from the node app to the Harmony hub. I'm hoping someone can dig in and find a way to use this same method to connect Hubitat directly to Harmony.
- It uses Hubitat Maker API to issue on/off commands to switches. These can be switches created by the ported Harmony Connect app from ST, virtual switches, physical switches, whatever.
- You will need to provide the node app your local Maker API endpoint, Harmony hub IP address (best to have it set as a static IP in your router), and a cross-reference between the activity IDs from Harmony and the corresponding switch you want to control in HE.
- If you have multiple Harmony hubs like I do, you will need a separate instance of the app running for each one.
- Switches are updated after the activity finishes running. I tried doing it before, but changing the Harmony Connect switch would trigger it to do a refresh, and since the activity wasn't done starting yet it was causing the switch to go off -> on -> off. This doesn't seem to happen when waiting until the end of the activity start up.
Download/extract harmonyhubjs-client from the link above, then run npm install inside the folder.
In my case, this is extracted to /home/pi/Harmony/harmonyhubjs-client folder.
Go up one level (in my case, to the /home/pi/Harmony folder) and create a new .js file. Call it whatever you want. You will need one for each hub you have. In my case, I have mbr.js (for master bedroom hub) and lr.js (for living room hub). Copy the code below and paste into your file.
var harmony = require('./harmonyhubjs-client')
var request = require('request')
const makerLink = ''
const activityXref = //create an array variable to associate activity IDs from Harmony to switch IDs from Hubitat
[
['',''], //Activity 1
['',''], //Activity 2
['',''], //Activity 3
['',''] //Activity 4
]
const harmonyIP = '192.168.0.101'
harmony(harmonyIP).then(function(harmonyClient) {
console.log('hub client started for ' + harmonyIP)
harmonyClient.getActivities()
.then(function (activities) {
console.log('activities found on hub')
console.log('-----------------------')
var i
for(i = 0; i < activities.length; i++) {
if(activities[i].label != 'PowerOff') {
console.log(activities[i].id + ' (' + activities[i].label + ')')
}
}
console.log('---------------------------')
console.log('listening for state digests')
})
! function keepAlive(){
harmonyClient.request('getCurrentActivity').timeout(5000).then(function(response) {
setTimeout(keepAlive, 45000);
}).catch(function(e){
//disconnected from hub
});
}();
harmonyClient.on('stateDigest', function(digest) {
var statusCode = digest.activityStatus
var currentlyOn = digest.runningActivityList
var targetActivity = digest.activityId
console.log('***received state digest***')
if (statusCode == '0') { //Hub is off`
if(currentlyOn == '' && targetActivity == '-1') {
console.log('hub is off')
} else {
console.log('activity ' + currentlyOn + ' is now off')
changeSwitch(currentlyOn, 'off')
}
}
if (statusCode == '1') { //Activity is starting
console.log('activity is starting')
}
if (statusCode == '2') { //Activity is started
if(currentlyOn == targetActivity) {
console.log('activity is started')
} else {
if(currentlyOn == '') {
console.log('activity ' + targetActivity + ' is now on')
changeSwitch(targetActivity, 'on')
}
else {
console.log('activity ' + targetActivity + ' is now on')
changeSwitch(targetActivity,'on')
console.log('activity ' + currentlyOn + ' is now off')
changeSwitch(currentlyOn,'off')
}
}
}
if (statusCode == '3') { //Hub is turning off
console.log('hub is turning off')
}
})
}).catch(function(e){
console.log('error')
});
function changeSwitch (activityId, changeTo) {
var activityCount = activityXref.length
var i
var switchId = ""
var tokenLabelPos = makerLink.indexOf('access_token')
var makerURL = makerLink.substring(0,tokenLabelPos - 1)
var makerToken = makerLink.replace(makerURL,'')
for(i = 0; i < activityCount; i++) {
if (activityXref[i][0] == activityId) {
switchId = activityXref[i][1]
console.log('xref activity ' + activityId + ' -> switch ' + switchId)
}
}
var options = {
uri: makerURL + "/" + switchId + "/" + changeTo + makerToken,
method: 'GET',
json: true
}
request(options, function(error, response, body) {
if (!error && response.statusCode == 200) {
console.log("sending " + changeTo + " command to switch " + switchId)
//console.log(body)
}
})
}