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

@@ -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();
},
};
},
};