Last active
December 6, 2016 20:59
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"dependencies": { | |
"node-hue-api": "^2.3.0", | |
"socket.io": "^1.7.1" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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