Before cleanup
This commit is contained in:
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
89
src/index.js
89
src/index.js
@@ -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
22
src/logger.js
Normal 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,
|
||||
};
|
||||
};
|
||||
112
src/mpd.js
112
src/mpd.js
@@ -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;
|
||||
};
|
||||
|
||||
95
src/pulse.js
95
src/pulse.js
@@ -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();
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user