Skip to content

Instantly share code, notes, and snippets.

@anekos
Created January 11, 2018 08:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anekos/f0fe155409d9e230a8765655d45b2dc3 to your computer and use it in GitHub Desktop.
Save anekos/f0fe155409d9e230a8765655d45b2dc3 to your computer and use it in GitHub Desktop.
Elgato Stream Deck の自分アプリ
// 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);
{
"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