Created
January 11, 2018 08:23
-
-
Save anekos/f0fe155409d9e230a8765655d45b2dc3 to your computer and use it in GitHub Desktop.
Elgato Stream Deck の自分アプリ
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
// http://www.iconshock.com/flat-icons/general-icons/audio-volume-none-icon | |
// https://www.svgrepo.com/svg/47144/air-conditioner | |
// https://www.flaticon.com/packs/characters-and-numbers | |
// https://github.com/Lange/node-elgato-stream-deck | |
// https://github.com/joshmarinacci/node-pureimage | |
const FS = require('fs'); | |
const HTTP = require('http'); | |
const PImage = require('pureimage'); | |
const Path = require('path'); | |
const RP = require('request-promise'); | |
const StreamDeck = require('elgato-stream-deck'); | |
const TMP = require('tmp'); | |
const exec = require('child_process').exec; | |
const SINK = 'alsa_output.pci-0000_00_1f.3.analog-stereo'; | |
const deck = new StreamDeck(); | |
var currentKeys = {}; | |
(() => { | |
function forceInterval(f) { | |
let ignore = false; | |
return function () { | |
if (ignore) | |
return; | |
ignore = true; | |
setTimeout(() => ignore = false, 250); | |
return f.apply(this, arguments) | |
}; | |
} | |
deck.on('down', forceInterval(index => { currentKeys[index] && currentKeys[index].onDown() })); | |
deck.on('up', index => { currentKeys[index] && currentKeys[index].onUp() }); | |
deck.on('error', error => { console.error(error); }); | |
})(); | |
function KeyAction(f) { | |
if (Array.isArray(f)) { | |
let actions = f.map(KeyAction); | |
return async () => { | |
for (let action of actions) { | |
await action(); | |
} | |
}; | |
} | |
if (typeof f === 'string') { | |
let command = f; | |
return () => { exec(command) }; | |
} | |
return f || (async () => {}); | |
} | |
function Key(image, onDown, onUp) { | |
return {image: image, onDown: KeyAction(onDown), onUp: KeyAction(onUp)}; | |
} | |
async function setImage (cellNumber, path) { | |
return deck.fillImageFromFile(parseInt(cellNumber), Path.resolve(__dirname, path)); | |
} | |
function updateVolume() { | |
exec('pamixer --sink ' + SINK + ' --get-mute', async (err, stdout, stderr) => { | |
if (!err) { | |
return deck.fillImageFromFile(14, 'images/audio_volume_mute.png'); | |
} | |
exec('pamixer --sink ' + SINK + ' --get-volume', async (err, stdout, stderr) => { | |
let font = PImage.registerFont('/usr/share/fonts/TTF/VL-Gothic-Regular.ttf','VL Gothic'); | |
font.load(async () => { | |
let tempFile = TMP.fileSync(); | |
let volume = parseInt(stdout.trim(), 10); | |
if (volume == 0) { | |
} | |
let baseImage = 0 < volume ? 'images/audio_volume_medium.png' : 'images/audio_volume_mute.png'; | |
let image = await PImage.decodePNGFromStream(FS.createReadStream(baseImage)); | |
let ctx = image.getContext('2d'); | |
ctx.fillStyle = '#ffffff'; | |
ctx.font = "20pt 'VL Gothic'"; | |
ctx.fillText(volume.toString(), 42, 20); | |
await PImage.encodePNGToStream(image, FS.createWriteStream(tempFile.name)); | |
await deck.fillImageFromFile(9, tempFile.name); | |
return FS.unlink(tempFile.name, () => {}); | |
}) | |
}); | |
}); | |
} | |
async function setup (keys) { | |
if (typeof keys == 'function') | |
keys = keys(); | |
if (currentKeys) | |
KeyAction(currentKeys.finalize)(); | |
currentKeys = keys; | |
for (let i = 0; i < 15; i++) { | |
if (keys[i].image) { | |
setImage(i, 'images/' + keys[i].image); | |
} else { | |
deck.fillColor(i, 0, 0, 0); | |
} | |
} | |
KeyAction(keys.after)(); | |
} | |
function httpGet(url) { | |
return () => RP(url); | |
} | |
async function matrixChangeChannel(output, input) { | |
return await RP('http://example.com/api/device/first/code/matrix-port-' + output + '-' + input + '/play'); | |
} | |
/** | |
* 4 3 2 1 0 | |
* 9 8 7 6 5 | |
* 14 13 12 11 10 | |
*/ | |
const Template = { | |
"0": Key(null), | |
"1": Key(null), | |
"2": Key(null), | |
"3": Key(null), | |
"4": Key(null), | |
"5": Key(null), | |
"6": Key(null), | |
"7": Key(null), | |
"8": Key(null), | |
"9": Key(null), | |
"10": Key(null), | |
"11": Key(null), | |
"12": Key(null), | |
"13": Key(null), | |
"14": Key(null), | |
"after": null, | |
}; | |
const DefaultKeys = () => { | |
let i = 0; | |
let animHandle = setInterval(async () => { | |
i++; | |
if (5 < i) | |
i = 1; | |
return deck.fillImageFromFile(0, 'images/e' + i + '.png'); | |
}, 100); | |
let levelHandle = setInterval(async () => { | |
}, 5000); | |
return { | |
"0": Key(null, 'twty ねこなめりんちょくらぶ'), | |
"1": Key('lamp-white.svg', httpGet('http://example.com/api/device/first,second/code/light-off/play')), | |
"2": Key('lamp-yellow.svg', httpGet('http://example.com/api/device/first,second/code/light-max/play')), | |
"3": Key('air-con-off.png', httpGet('http://example.com/api/task/air-all-off/play')), | |
"4": Key('air-con-on.png', httpGet('http://example.com/api/task/air-all-on/play')), | |
"5": Key('monitor.png', 'xset dpms force off'), | |
"6": Key(null), | |
"7": Key('audio_volume_high.png', ['pamixer --sink ' + SINK + ' --increase 5', updateVolume]), | |
"8": Key('audio_volume_low.png', ['pamixer --sink ' + SINK + ' --decrease 5', updateVolume]), | |
"9": Key(null, ['pamixer --sink ' + SINK + ' --toggle-mute', updateVolume]), | |
"10": Key(null), | |
"11": Key(null), | |
"12": Key(null), | |
"13": Key('computer-display.svg', () => setup(DisplayKeys)), | |
"14": Key('multi-display.png', () => setup(MatrixKeys)), | |
"after": updateVolume, | |
"finalize": () => { | |
clearInterval(animHandle); | |
clearInterval(levelHandle); | |
} | |
}; | |
}; | |
const MatrixKeys = { | |
"0": Key('letter-a.svg', httpGet('http://example.com/api/device/first/code/matrix-out-a/play')), | |
"1": Key('blue-four.svg', httpGet('http://example.com/api/device/first/code/matrix-port-a-4/play')), | |
"2": Key('blue-three.svg', httpGet('http://example.com/api/device/first/code/matrix-port-a-3/play')), | |
"3": Key('blue-two.svg', httpGet('http://example.com/api/device/first/code/matrix-port-a-2/play')), | |
"4": Key('blue-one.svg', httpGet('http://example.com/api/device/first/code/matrix-port-a-1/play')), | |
"5": Key('letter-b.svg', httpGet('http://example.com/api/device/first/code/matrix-out-b/play')), | |
"6": Key('blue-four.svg', httpGet('http://example.com/api/device/first/code/matrix-port-b-4/play')), | |
"7": Key('blue-three.svg', httpGet('http://example.com/api/device/first/code/matrix-port-b-3/play')), | |
"8": Key('blue-two.svg', httpGet('http://example.com/api/device/first/code/matrix-port-b-2/play')), | |
"9": Key('blue-one.svg', httpGet('http://example.com/api/device/first/code/matrix-port-b-1/play')), | |
"10": Key('power-button.svg', httpGet('http://example.com/api/device/first/code/matrix-power/play')), | |
"11": Key('swap-arrows.svg', httpGet('http://example.com/api/device/first/code/matrix-audio-switch/play')), | |
"12": Key(null), | |
"13": Key(null), | |
"14": Key('back.svg', setup.bind(null, DefaultKeys)), | |
}; | |
const DisplayKeys = { | |
"0": Key('audio_volume_mute.png', httpGet('http://example.com/api/device/first/code/left-mute/play')), | |
"1": Key('power.svg', httpGet('http://example.com/api/device/first/code/left-power/play')), | |
"2": Key(null), | |
"3": Key('hdmi.svg', httpGet('http://example.com/api/device/first/code/left-hdmi/play')), | |
"4": Key('dvi.svg', httpGet('http://example.com/api/device/first/code/left-dvi/play')), | |
"5": Key('audio_volume_mute.png', httpGet('http://example.com/api/device/first/code/right-mute/play')), | |
"6": Key('power.svg', httpGet('http://example.com/api/device/first/code/right-power/play')), | |
"7": Key('hdmi.svg', httpGet('http://example.com/api/device/first/code/right-hdmi2/play')), | |
"8": Key('hdmi.svg', httpGet('http://example.com/api/device/first/code/right-hdmi1/play')), | |
"9": Key('dvi.svg', httpGet('http://example.com/api/device/first/code/right-dvi/play')), | |
"10": Key(null), | |
"11": Key(null), | |
"12": Key(null), | |
"13": Key('back.svg', setup.bind(null, DefaultKeys)), | |
"14": Key(null), | |
"after": null, | |
}; | |
setup(DefaultKeys); | |
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
{ | |
"name": "my-stream-deck", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"dependencies": { | |
"commander": "^2.12.2", | |
"elgato-stream-deck": "^2.0.0", | |
"pureimage": "^0.1.3", | |
"request-promise": "^4.2.2", | |
"tmp": "0.0.33" | |
}, | |
"devDependencies": {}, | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "", | |
"license": "ISC" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment