Lets use the Stream Deck to control a HomeKit device.

I get warm in the computer room occasionaly, so I have a small fan I turn on and off frequently. I also like to wear big wired headphones. I never take my headphones off when I reach for the fan. So I'm constantly reaching the end of my headphone cord. Well, I just got this Elgato Stream Deck. And, I have some smart outlets that I can control with HomeKit. But, The Stream Deck does not have a plugin for HomeKit, at least not that I found.

The smart plug I'm using is the koogeek Smart Plug It's a pain to setup with homekit, I have one that I can not get to work. :) But, I have the fan connected to one and I can control it with homekit.

Now how do I connect it to the Stream Deck?

I have HomeBridge running on a server. And I have added it to HomeKit. In HomeBridge I added the Homebridge Http Switch plugin. This plugin has 3 rest inputs that it can call for turning the light on, off, and checking it's status. It expected 1 and 0 for the status call. No idea why it's not expecting json and boolean, but so be it. I'll wright my own plugin later.

The Stream Deck has an action called MCControl this can connect to a rest endpoint as well. But it has some quirks as well. It does do a post for setting, but instead of json it sends plain/text. I'll end up writing an action that works better here, but again this works, if you have an endpoint that can handle the quirky behavior. Maybe this is based on some standard I'm not aware of but, its not how I would have done it.

In between these I have a small Node JS app. It keeps track of switch state, and provides apis for the Stream Deck action and the HomeBridge plugin to talk to.

const port = 3000;

const express = require('express');
const bodyParser = require('body-parser');
let app = express();
app.use(bodyParser.text({ type: 'text/*' }))

var switches = {};


function getSwitchValue(switchName, trueValue, falseValue) {

	let value = switches[switchName];

	if (trueValue === undefined) {

		trueValue = true;
	} 

	if (falseValue == undefined) {

		falseValue = false;
	}

	if (value === undefined || value == false) {

		return falseValue;

	} else {
		
		return trueValue;
	}
}

function setSwitchValue(switchName, valueAsBoolean) {

	// Don't care about current state of switch - or if it exists or not at this point.
	switches[switchName] = valueAsBoolean;
}

// ==================
// General purpose getter to show the state of all switches in the browser.

app.get('/', (request, response) => {
	
	// return current state of all known switches
	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify(switches, null, 4));	
});


// HomeBridge http switch end points.
app.get('/switches/:switchName/on', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchOn called for switch: ", switchName);
	setSwitchValue(switchName, true);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});

app.get('/switches/:switchName/off', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchOff called for switch: ", switchName);

	setSwitchValue(switchName, false);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});

app.get('/switches/:switchName/status', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("switchStatus called for switch: ", switchName);

	let switchValue = getSwitchValue(switchName, "1", "0");
	response.setHeader('Content-Type', 'text/plain');
	response.send(switchValue);	
});


// ==================
// Stream Deck MCControl endpoints.

app.get('/switches/:switchName', (request, response) => {

	let switchName = request.params.switchName;

	//console.log("got status request for switch: ", switchName);

	let switchValue = getSwitchValue(switchName, "ON", "OFF");

	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify({ state: switchValue }));
});


app.post('/switches/:switchName', (request, response) => {
	
	let switchName = request.params.switchName;

	//console.log("switch set post called for switch: ", switchName);

	var data = request.body;
	console.log("Data: ", data);

	if (data == "ON") {

		switchState = true;

	} else if (data == "OFF") {

		switchState = false;
	}

	setSwitchValue(switchName, switchState);

	let switchValue = getSwitchValue(switchName, "ON", "OFF");
	response.setHeader('Content-Type', 'application/json');
	response.send(JSON.stringify({ state: switchValue }));
});



app.listen(port, (err) => {
	if (err) {
		return console.log('something bad happened', err)
	}

	console.log(`server is listening on ${port}`)
});

I run this little server on the same server where I have HomeBridge.

Now for the final piece. Automations in HomeKit. I have a couple of automations in HomeKit that do the following:

  • When the Switch turns on, turn the fan on.
  • When the Switch turns off, turn the fan off.

There is a bit of a delay when I use the Stream Deck due, to a configured polling interval, but it works like a charm. And, if I use HomeKit to control the fan directly. It updates via HomeBridge, and the Stream Deck switch reflects the correct state.

Nice and Simple...

It's just that simple

Prev Post Next Post