Skip to content

Instantly share code, notes, and snippets.

@mrpapercut
Last active December 6, 2016 20:59
Show Gist options
  • Save mrpapercut/2a46b61e1ce549ded061b126fc792c42 to your computer and use it in GitHub Desktop.
Save mrpapercut/2a46b61e1ce549ded061b126fc792c42 to your computer and use it in GitHub Desktop.
Webcam to Philips Hue. Logical file order: package.json, server.js, api.js, index.html
'use strict';
import {HueApi, lightState} from 'node-hue-api';
import creds from './constants/credentials'; // JSON object containing Hue Bridge ip-address and user credentials
class Api {
constructor() {
this.api = new HueApi(creds.hue.hostname, creds.user.username);
this.lights = [];
}
_onError(err) {
console.error(err);
}
getConfig(cb) {
this.api.config((err, config) => {
if (err) this._onError(err);
else cb(config);
});
}
getLights(cb) {
this.api.getLights((err, {lights}) => {
if (err) this._onError(err);
else this.setLights(lights), cb(lights);
});
}
setLights(lights) {
this.lights = lights;
}
getLightStatus(lightId, cb) {
this.api.lightStatusWithRGB(lightId, ((err, {state}) => {
if (err) this._onError(err);
else cb(state);
}));
}
setLightState(lightId, state) {
this.api.setLightState(lightId, state);
}
createLS(cmd, args) {
return lightState.create()[cmd](args);
}
setColor(color) {
// Hardcoding lightId for reasons
this.setLightState(3, lightState.create().rgb(this.hexToRGB(color)));
this.setLightState(4, lightState.create().rgb(this.hexToRGB(color)));
}
hexToRGB(hex) {
return hex.split(/([a-f0-9]{2})/).filter(c => c && !isNaN(parseInt(c, 16))).map(c => parseInt(c, 16));
}
rgbToHex(rgb) {
return '#' + rgb.map(c => ('0' + c.toString(16)).substr(-2));
}
}
export default Api;
<!doctype html>
<html>
<head>
<title>Webcam to Hue</title>
<script src="Vibrant.min.js"></script>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
</head>
<body>
<button id="stop" type="button">Stop</button>
<video autoplay></video>
<div id="color" style="width:50px;height:50px;display:block"></div>
<script>
const video = document.querySelector('video');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
const colorPreview = document.querySelector('#color');
let localMediaStream = null;
// Establish websocket connection
const socket = io.connect('http://localhost:8080');
// Simple websocket emitter
const emitSocket = obj => socket.emit('huecommand', obj);
// Send color to backend
const setColor = hex => {
// Show found color on page (for debugging)
colorPreview.style.backgroundColor = hex;
// Emit using websocket
emitSocket({
cmd: 'setColor',
args: hex
});
};
// Returns hex-code from image
const getHexFromImage = img => {
// Use Vibrant.js to find most prominent color
const vibrant = new Vibrant(img);
const swatches = vibrant.swatches();
return swatches.Vibrant.getHex();
};
// Take screenshot from webcam stream and set as source for image
const takeScreenshot = () => {
if (localMediaStream) {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
img.src = canvas.toDataURL('image/webp');
}
};
// When image is loaded, extract color from image and send to backend
img.onload = () => {
const hex = getHexFromImage(img);
setColor(hex.replace('#', ''));
};
// Retrieve webcam stream. If stream is not available, user will be asked for permission
navigator.getUserMedia({
video: true
}, stream => {
video.src = window.URL.createObjectURL(stream);
localMediaStream = stream;
}, error => {
console.error('Not allowed to stream webcam', error);
});
// Take a screenshot every 800ms
const interval = window.setInterval(() => takeScreenshot(), 800);
// Button to kill the interval
document.querySelector('#stop').addEventListener('click', () => window.clearInterval(interval));
</script>
</body>
</html>
{
"dependencies": {
"node-hue-api": "^2.3.0",
"socket.io": "^1.7.1"
}
}
// Simple websockets-based server
import socketio from 'socket.io';
import Api from './api';
const port = 8080;
const api = new Api();
const io = socketio(port);
io.on('connection', socket => {
console.log('client connected');
socket.on('huecommand', ({cmd, args}) => {
// This is pretty awful because no checks at all:
// If function exists in Api, call it
if (api[cmd] instanceof Function) {
api[cmd](args);
}
});
socket.on('disconnect', () => console.log('client disconnected'));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment