[Alpha] Harmony hub instant status updates using node.js app


#62

Ok here is the process I have written up. I want to help those who have no idea what to do and also give them access to the Hubitat using OpenVPN too. Anyone want to try it? The code I am using is attached here as well.

app.js code:

var sleep = require('sleep');
var harmony = require('harmonyhubjs-client')
	var request = require('request')
	const makerLink = 'http://<your_hubitat_ip>/apps/api/<your_MAKERAPI_ID>/devices?access_token=<your_MAKERAPI_access_token>'
	const activityXref = //create an array variable to associate activity IDs from Harmony to switch IDs from Hubitat
						[
							['<Harmony Activity ID>','<HARMONY_DEVICE_MAKER_ID>'], //Activity 1
							['<Harmony Activity ID>','<HARMONY_DEVICE_MAKER_ID>'], //Activity 2
							['<Harmony Activity ID>','<HARMONY_DEVICE_MAKER_ID>'], //Activity 3
						]
	const harmonyIP = '<your_harmony_ip>'

	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, 'refresh')
				}
			}
			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, 'refresh')
					}
					else {
						console.log('activity ' + targetActivity + ' is now on')
						changeSwitch(targetActivity,'refresh')
						console.log('activity ' + currentlyOn + ' is now off')					
						changeSwitch(currentlyOn,'refresh')
					}
				}
			}
			if (statusCode == '3') { //Hub is turning off
				console.log('hub is turning off')
			}
		})
	}).catch(function(e){
		console.log('error')
	});



	function changeSwitch (activityId, changeTo) {
		sleep.sleep(3)
		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,
			//uri: makerURL + "/" + switchId + "/Refresh/" + 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)
			} 
		})
	}

The Setup Script:

Flash Image Raspi-lite using Etcher
	passwd pi
	sudo passwd root
	sudo apt-get update
	sudo apt-get dist-upgrade

Install Samba, Winbind and Share
	sudo apt-get install samba samba-common-bin
	sudo apt-get install winbind
	sudo mkdir -m 1777 /share
	sudo nano/etc/samba/smb.conf
		(add to bottom of file)
		
		[share]
		Comment = Pi shared folder
		Path = /share
		Browseable = yes
		Writeable = Yes
		only guest = no
		reate mask = 0777
		directory mask = 0777
		Public = yes
		Guest ok = yes

	sudo smbpasswd -a pi
	sudo /etc/init.d/samba restart
	sudo nano /etc/nsswitch.conf
		(add wins to the hosts: line)
	sudo nano /etc/hostname
		(change to your hostname preference)
	sudo nano /etc/hosts
		(change to your hostname preference)
	sudo reboot

Install OpenVPN (optional)
	curl -L https://install.pivpn.io | bash
		(follow all basic recommendations)
	sudo mkdir /share/ovpns
	sudo reboot

Setup Certs for OpenVPN	(optional)
	pivpn -a
		(enter client name and password; repeat as necessary for each client)
	sudo cp ~/ovpns/* /share/ovpns	

Install Node.JS
	curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash -
	sudo apt install -y nodejs

Install Forever and forever-service (must be SuperUser)
	su
	npm install -g forever
	npm install -g forever-service

Install Sleep Service (must be SuperUser)
	su
	cd /share/harmony
	npm install sleep --save

Install HarmonyHub Real-time Updates
	mkdir /share/harmony	
	cd /share/harmony
	su
	npm install harmonyhubjs-client --save

   Setup Hubitat for HarmonyHub Real-time Updates with use of Logitech Harmony (Connect) [https://github.com/mattw01/HubitatHarmony]
	http://portal.hubitat.com
	Install MakerAPI in Apps
	Add Harmony Devices installed by Logitech Harmony (Connect) App
	Note MakerAPI ID and Access Token "http://<your_hubitat_ip>/apps/api/[MAKER_API]/devices?access_token=<your_token>"
	Click "Get All Devices" to acquire Hubitat ID for your Harmony Devices

   Continue Setup
	cd /share/harmony
	nano app.js
	    (copy raw code from code repository)
	    - Change makerLink with your information above
	    - Change Activity section ['harmony activity','MAKER_ID activity'] per activity.  Delete any additional activities or add accordingly
            - Change HarmonyIP to reflect your Harmony IP
	    - save/exit
	node app.js
	    (take note of activity name and activity #)
	CTRL-C
	nano app.js
	    (modify each activity to coorespond with MAKER_ID activity)
	node app.js
	    (if no errors test your harmony hub devices and monitor realtime updates in Hubitat)

    Set service to run on boot as a service
	cd /share/harmony
	sudo forever-service install harmonyhub
	sudo service harmonyhub status
		(if no errors test your harmony hub devices and monitor realtime updates in Hubitat)

#63

Ok, so I used your latest post as a guide and managed to get it up and going for the most part. The problem is, my use case is more in line with what @destructure00 seems to be doing. At least in the sense that I have multiple hubs and am looking to have virtual switches turn on or off based on whether an activity is running. Is there any way to use the client.js template he posted up above (post #25) as a replacement for the app.js template you provided? As it stands now, I have the app up and running on my Raspberry Pi, but I don't have it actually connected to my Hubitat since I'm not using the Harmony Connect app.


#64

You can use @destructure00 version too. If you have switches in HUbitat than you can just use those in the MakerAPI app. No difference really. I refresh verses turn on/off. We are working on another version that enables us to select either or both depending on the situation.


#65

From looking through your code, I think I see how I can change the "refresh" commands to either "on" or "off" where appropriate. Is there a way to easily add my second hub to your script?

Also, for some reason I started running into an issue this morning. As of last night I could get the script up and running and could watch hub updates in my ssh terminal. This morning I deleted the app.js file and decided to remake it with the changes needed to control virtual switches. For some reason, when I run "node app.js" now, I get the following error.

SyntaxError: Invalid or unexpected token
at new Script (vm.js:83:7)
at createScript (vm.js:267:10)
at Object.runInThisContext (vm.js:319:10)
at Module._compile (internal/modules/cjs/loader.js:685:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:733:10)
at Module.load (internal/modules/cjs/loader.js:620:32)
at tryModuleLoad (internal/modules/cjs/loader.js:560:12)
at Function.Module._load (internal/modules/cjs/loader.js:552:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:775:12)
at startup (internal/bootstrap/node.js:300:19)

Not sure what changed and how to fix it.


#66

I would go back to your working app.js script. You probably have a missing ending operator.

Are you using the forever-service to boot as a service? If so you will need to copy your /share/harmony to /share/harmony2 and then just execute the foforever-service but use harmonyhub2 as the service name. Obviously modify your app.js file accordingly.


#67

I'm going to work my way through your walkthru again to see if I can figure out where I went wrong. I did notice a couple of issues the first time I went through it. The two steps below seemed to be out of order. When I tried to install the Sleep Service I got an error that the directory wasn't there. It seems like the /share/harmony folder isn't created until the next step. I just did that part first, then went back to the Sleep Service step.

Install Sleep Service (must be SuperUser)
su
cd /share/harmony
npm install sleep --save

Install HarmonyHub Real-time Updates
mkdir /share/harmony	
cd /share/harmony
su
npm install harmonyhubjs-client --save

I also ran into an issue because I didn't have Git installed on my Raspberry Pi. I had to go install that at one point to fix an error.

UPDATE
Found one other issue in the walkthru. I think there's a space missing after the nano command in the step below.

sudo nano/etc/samba/smb.conf

#68

Ok, I think I managed to get everything working. The error I reported this morning seemed to have something to do with me copying the app.js template into TextEdit to make my changes rather than pasting directly to the RPi via SSH and making the changes in nano. It was easier for me to navigate via TextEdit, but obviously something got messed up somewhere in the transition. I followed your directions on creating a second folder and service. As far as I can tell, everything is working as it should. Thanks again for all the assistance.


#69

Go out ad download Visual Studio Editor. Best tool for all of this without messing up your code. :slight_smile:


#70

Is there any reason why I couldn't run this and KuKuHarmony on the same Raspberry Pi 2?


#71

Should be able to. I run a lot of stuff on my pi. I just got my third pi so I can do custom HUE/Tradfri integration with Hubitat. Working on that now. Also I use a Pi to control my 3d printer.


#72

It seems to be working so far. Thanks again.