Before cleanup

This commit is contained in:
Lewis Diamond 2020-12-19 19:34:27 -05:00
parent 290a37fd02
commit 45e363536a
8 changed files with 412 additions and 40 deletions

View File

@ -7,8 +7,11 @@
"chroma-js": "^2.1.0",
"dbus": "^1.0.7",
"dbus-native": "^0.4.0",
"debug": "^4.3.1",
"mpd": "^1.3.0",
"suncalc": "^1.8.0"
"paclient": "^0.0.2",
"suncalc": "^1.8.0",
"yargs": "^16.2.0"
},
"devDependencies": {
"prettier": "^2.1.2"

View File

@ -8,7 +8,7 @@ const colors = {
};
function brightness(color, pct) {
return chroma(color).set("hsv.v", pct);
return chroma(color).set("hsv.v", pct / 100);
}
module.exports = {

View File

@ -88,7 +88,7 @@ function staticMatrix(r, g, b, rows, cols) {
}
function setKey(matrix, row, col, r, g, b) {
const idx = row * (col + 1) * 3 + (col + 1) * 3;
const idx = row * (matrix.cols + 1) * 3 + (col + 1) * 3;
matrix[idx] = r;
matrix[idx + 1] = g;
matrix[idx + 2] = b;

View File

@ -1,11 +1,54 @@
#!/bin/env node
const { execSync, spawnSync } = require("child_process");
const debug = require("debug");
const { spawnSync } = require("child_process");
const yargs = require("yargs");
const suncalc = require("suncalc");
const dbus = require("./dbus");
const pulse = require("./pulse");
const mpd = require("./mpd");
const { colors, brightness } = require("./colors");
const DANGEROUS_VOLUME = 40;
const AUDIO_NAME = "analog-surround-51";
const MIC_NAME = "C-Media_Electronics_Inc._USB_PnP_Sound_Device";
const o = yargs.options({
a: {
alias: "audio",
default: "analog-surround-51",
describe: "String used to match the pulse audio device",
type: "string",
},
m: {
alias: ["mic", "record"],
default: "C-Media_Electronics_Inc._USB_PnP_Sound_Device",
describe: "String used to match the pulse microphone device",
type: "string",
},
v: {
alias: "verbose",
default: 0,
describe: "Verbosity level, add more -v for more verbosity",
type: "count",
},
mpdport: {
alias: "p",
default: 6600,
describe: "MPD port",
type: "number",
},
mpdhost: {
alias: "h",
default: "localhost",
describe: "MPD Host",
type: "string",
},
}).argv;
const AUDIO_NAME = o.a;
const MIC_NAME = o.m;
const levels = ["*:error", "*:warn", "*:info", "*:log", "*:debug"].slice(
0,
o.v + 1,
);
debug.enable(levels.join(","));
function isNight() {
const now = new Date();
@ -14,18 +57,6 @@ function isNight() {
return isnight;
}
function setPlaying(matrix) {
const playing = !spawnSync("mpc | grep playing", { shell: true }).status;
[18, 19, 20].forEach(x =>
dbus.setKey(
matrix,
0,
x,
...(playing ? colors.ALERT_COLOR.rgb() : colors.BASE_COLOR.rgb()),
),
);
}
function getBrightness() {
return isNight() ? 60 : 100;
}
@ -79,7 +110,7 @@ async function setAudio(matrix) {
volume < DANGEROUS_VOLUME
? colors.BASE_COLOR.rgb()
: colors.ALERT_COLOR.rgb(),
volume / 50,
(volume / 50) * 100,
);
keys.forEach(x => dbus.setKey(matrix, 0, x, ...c.rgb()));
}
@ -96,4 +127,26 @@ async function run() {
dbus.disconnect();
}
run();
async function daemon() {
const matrix = dbus.staticMatrix(...colors.BASE_COLOR.rgb(), 6, 22);
const setKey = (...args) => {
dbus.setKey(matrix, ...args);
};
const interfaces = await dbus.connect();
const draw = async () => await interfaces.chroma.setKeyRow(matrix);
const mpdDisconnect = mpd(
{ host: o.mpdhost, port: o.mpdport },
setKey,
draw,
)();
const p = pulse.connect({ out: AUDIO_NAME, mic: MIC_NAME, setKey, draw });
process.on("SIGINT", () => {
mpdDisconnect();
dbus.disconnect();
p.disconnect();
});
}
daemon();

22
src/logger.js Normal file
View File

@ -0,0 +1,22 @@
const _debug = require("debug");
const application = "kb.js";
module.exports = ({ prefix = "global" } = {}) => {
const log = _debug(`${application}:${prefix}:log`);
const debug = _debug(`${application}:${prefix}:debug`);
const info = _debug(`${application}:${prefix}:info`);
const error = _debug(`${application}:${prefix}:error`);
const warn = _debug(`${application}:${prefix}:warn`);
log.log = console.log.bind(console);
debug.log = console.log.bind(console);
info.log = console.info.bind(console);
warn.log = console.warn.bind(console);
error.log = console.error.bind(console);
return {
log,
debug,
info,
warn,
error,
};
};

View File

@ -1,18 +1,94 @@
var mpd = require('mpd'),
cmd = mpd.cmd
var client = mpd.connect({
port: 6600,
host: 'localhost',
});
client.on('ready', function() {
console.log("ready");
});
client.on('system', function(name) {
console.log("update", name);
});
client.on('system-player', function() {
client.sendCommand(cmd("status", []), function(err, msg) {
if (err) throw err;
console.log(msg);
const { EventEmitter } = require("events");
const net = require("net");
const { spawnSync } = require("child_process");
const { colors } = require("./colors");
const { debug, log, info, error } = require("./logger")("mpd");
class Client extends EventEmitter {
socket;
constructor(host, port, retry = true) {
super();
this.options = { host, port };
this.connect();
this.retry = retry;
}
connect() {
if (this.socket) {
try {
this.socket.destroy();
} catch (e) {
error(e);
}
}
this.socket = net.connect(this.options, this.onConnect.bind(this));
this.socket.setEncoding("utf8");
this.socket.on("data", this.onData.bind(this));
this.socket.on("close", this.onClose.bind(this));
this.socket.on("error", this.onError.bind(this));
}
close() {
this.retry = false;
this.socket.destroy();
this.removeAllListeners();
}
onConnect(...args) {
this.emit("connect");
this.socket.write("status\n");
}
onData(m) {
debug("data", m);
if (m.includes("changed: player")) {
this.socket.write("status\n");
} else if (m.includes("state: ")) {
this.emit("playing", m.includes("state: play"));
this.socket.write("idle player\n");
}
}
onClose(...args) {
this.emit("playing", false);
if (this.retry) {
error("Retrying in close");
setTimeout(this.connect.bind(this), 2000);
}
debug("close", ...args);
}
onError(...args) {
error("error", ...args);
this.emit("playing", false);
}
}
module.exports = ({ host, port }, setKey, draw) => {
const setPlaying = playing => {
info("playing", playing);
[18, 19, 20].forEach(x =>
setKey(
0,
x,
...(playing
? colors.ALERT_COLOR.rgb()
: colors.BASE_COLOR.rgb()),
),
);
draw();
};
const connect = () => {
const c = new Client(host, port);
c.on("connect", () => {
info("Connected to MPD");
});
});
c.on("playing", setPlaying);
return () => {
try {
c.close();
log("Closing mpd");
} catch (e) {
error(e);
}
};
};
return connect;
};

View File

@ -0,0 +1,95 @@
const PAClient = require("paclient");
const { colors, brightness } = require("./colors");
const { readFileSync } = require("fs");
const DANGEROUS_VOLUME = 0.4;
function getFnFromType(type) {
var fn;
switch (type) {
case "sink":
case "card":
case "source":
fn = type;
break;
case "sinkInput":
case "sourceOutput":
case "client":
case "module":
fn = `${type}ByIndex`;
break;
default:
throw new Error("Unexpected type: " + type);
}
return "get" + fn[0].toUpperCase() + fn.slice(1);
}
const setColor = (muted, volume, setKey) => {
const keys = [21];
if (muted) {
keys.forEach(x => setKey(0, x, ...colors.SAFE_COLOR.rgb()));
} else if (volume === 0) {
keys.forEach(x => setKey(0, x, ...colors.BLUE.rgb()));
} else {
const c = brightness(
volume < DANGEROUS_VOLUME
? colors.BASE_COLOR.rgb()
: colors.ALERT_COLOR.rgb(),
(volume / DANGEROUS_VOLUME) * 100,
);
keys.forEach(x => setKey(0, x, ...c.rgb()));
}
};
module.exports = {
connect({
host = "127.0.0.1",
cookiePath = `${process.env.HOME}/.config/pulse/cookie`,
setKey = (...args) => {
console.log("setKey", ...args);
},
draw = () => {},
} = {}) {
const pa = new PAClient();
const cookie = Buffer.from(
readFileSync(cookiePath, "binary"),
"binary",
);
pa.connect({ host: "127.0.0.1", cookie });
pa.on("ready", () => {
pa.subscribe(["sink", "source"]);
})
.on("change", (type, index) => {
pa[getFnFromType(type)](index, (err, info) => {
if (err) {
return;
}
if (info.name.includes("analog-surround-51")) {
const { muted, baseVolume, channelVolumes } = info;
const vol = Math.max(...channelVolumes) / baseVolume;
setColor(muted, vol, setKey);
draw();
}
if (info.name.includes("USB_PnP_Sound_Device")) {
const { muted } = info;
const c = muted
? colors.BASE_COLOR
: colors.ALERT_COLOR;
const rgb = c.rgb();
for (let i = 0; i < 6; ++i) {
for (let j = 0; j < 18; ++j) {
setKey(i, j, ...rgb);
}
}
draw();
}
});
})
.on("remove", (type, index) => {
console.log(`Removed ${type}, index #${index}`);
});
return {
disconnect() {
pa.end();
},
};
},
};

125
yarn.lock
View File

@ -10,6 +10,18 @@ abstract-socket@^2.0.0:
bindings "^1.2.1"
nan "^2.12.1"
ansi-regex@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
ansi-styles@^4.0.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
bindings@^1.2.1:
version "1.5.0"
resolved "https://npm.dev.jogogo.co/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
@ -24,6 +36,27 @@ chroma-js@^2.1.0:
dependencies:
cross-env "^6.0.3"
cliui@^7.0.2:
version "7.0.4"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
cross-env@^6.0.3:
version "6.0.3"
resolved "https://npm.dev.jogogo.co/cross-env/-/cross-env-6.0.3.tgz#4256b71e49b3a40637a0ce70768a6ef5c72ae941"
@ -57,16 +90,33 @@ dbus-native@^0.4.0:
dbus@^1.0.7:
version "1.0.7"
resolved "https://npm.dev.jogogo.co/dbus/-/dbus-1.0.7.tgz#8ec719b0943f1e77deab2d13ce74a6e5e9839e83"
resolved "https://registry.yarnpkg.com/dbus/-/dbus-1.0.7.tgz#8ec719b0943f1e77deab2d13ce74a6e5e9839e83"
integrity sha512-qba6/ajLoqzCy3Kl3aFgLXLP4TTf0qfgNjib1qoCJG/8HbSs0lDvxkz4nJU63CURZVzxvpK/VpQpT40KA8Kr3A==
dependencies:
nan "^2.14.0"
debug@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
ms "2.1.2"
duplexer@^0.1.1, duplexer@~0.1.1:
version "0.1.2"
resolved "https://npm.dev.jogogo.co/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
event-stream@^4.0.0:
version "4.0.1"
resolved "https://npm.dev.jogogo.co/event-stream/-/event-stream-4.0.1.tgz#4092808ec995d0dd75ea4580c1df6a74db2cde65"
@ -90,11 +140,21 @@ from@^0.1.7:
resolved "https://npm.dev.jogogo.co/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=
get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
hexy@^0.2.10:
version "0.2.11"
resolved "https://npm.dev.jogogo.co/hexy/-/hexy-0.2.11.tgz#9939c25cb6f86a91302f22b8a8a72573518e25b4"
integrity sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
isexe@^2.0.0:
version "2.0.0"
resolved "https://npm.dev.jogogo.co/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@ -120,6 +180,11 @@ mpd@^1.3.0:
resolved "https://registry.yarnpkg.com/mpd/-/mpd-1.3.0.tgz#a9a0e028f6808e5594f76fa9f0c574ad86f0c0dd"
integrity sha1-qaDgKPaAjlWU92+p8MV0rYbwwN0=
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nan@^2.12.1, nan@^2.14.0:
version "2.14.2"
resolved "https://npm.dev.jogogo.co/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
@ -133,6 +198,11 @@ optimist@^0.6.1:
minimist "~0.0.1"
wordwrap "~0.0.2"
paclient@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/paclient/-/paclient-0.0.2.tgz#1ab74a4bb8aa8b4de0f492c2f2bfc7b28629a0a2"
integrity sha1-GrdKS7iqi03g9JLC8r/HsoYpoKI=
path-key@^3.1.0:
version "3.1.1"
resolved "https://npm.dev.jogogo.co/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
@ -155,6 +225,11 @@ put@0.0.6:
resolved "https://npm.dev.jogogo.co/put/-/put-0.0.6.tgz#30f5f60bd6e4389bd329e16a25386cbb2e4a00a3"
integrity sha1-MPX2C9bkOJvTKeFqJThsuy5KAKM=
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
safe-buffer@^5.1.1:
version "5.2.1"
resolved "https://npm.dev.jogogo.co/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
@ -192,6 +267,22 @@ stream-combiner@^0.2.2:
duplexer "~0.1.1"
through "~2.3.4"
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
strip-ansi@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
dependencies:
ansi-regex "^5.0.0"
suncalc@^1.8.0:
version "1.8.0"
resolved "https://npm.dev.jogogo.co/suncalc/-/suncalc-1.8.0.tgz#1d9898109563078750f4994a959e654d876acbf5"
@ -214,6 +305,15 @@ wordwrap@~0.0.2:
resolved "https://npm.dev.jogogo.co/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
xml2js@^0.4.17:
version "0.4.23"
resolved "https://npm.dev.jogogo.co/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
@ -226,3 +326,26 @@ xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://npm.dev.jogogo.co/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
y18n@^5.0.5:
version "5.0.5"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18"
integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
yargs-parser@^20.2.2:
version "20.2.4"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
yargs@^16.2.0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.0"
y18n "^5.0.5"
yargs-parser "^20.2.2"