#!/bin/env node 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 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(); const night = suncalc.getTimes(now, 45.524128, -73.735409); const isnight = now >= night.sunset || now <= night.sunrise; return isnight; } function getBrightness() { return isNight() ? 60 : 100; } const volume = (Math.min( parseInt( spawnSync( `pactl list | grep -E "Name: .*${AUDIO_NAME}$|Volume" | grep "Name:" -A1 | tail -1 | cut -d% -f1 | cut -d/ -f2 | tr -d " "`, { shell: true }, ).stdout, ), 30, ) / 30) * 100; async function setMutedMic() { const muted = !spawnSync( `pacmd dump | grep ^set-source-mute.*${MIC_NAME}.*yes`, { shell: true }, ).status; const b = muted ? getBrightness() : 100; return dbus.staticMatrix( ...(muted ? brightness(colors.BASE_COLOR, b).rgb() : brightness(colors.ALERT_COLOR, b).rgb()), 6, 22, ); } async function setAudio(matrix) { const keys = [21]; const muted = !spawnSync( `pacmd dump | grep ^set-sink-mute.*${AUDIO_NAME}.*yes`, { shell: true }, ).status; if (muted) { keys.forEach(x => dbus.setKey(matrix, 0, x, ...colors.SAFE_COLOR.rgb()), ); } else if (volume === 0) { keys.forEach(x => dbus.setKey(matrix, 0, x, ...colors.BLUE.rgb())); } else { const volume = parseInt( spawnSync( `pactl list | grep -E "Name: .*${AUDIO_NAME}$|Volume" | grep "Name:" -A1 | tail -1 | cut -d% -f1 | cut -d/ -f2 | tr -d " "`, { shell: true }, ).stdout, ); const c = brightness( volume < DANGEROUS_VOLUME ? colors.BASE_COLOR.rgb() : colors.ALERT_COLOR.rgb(), (volume / 50) * 100, ); keys.forEach(x => dbus.setKey(matrix, 0, x, ...c.rgb())); } } async function run() { const interfaces = await dbus.connect(); const matrix = await setMutedMic(); setAudio(matrix); setPlaying(matrix); await interfaces.brightness.setBrightness(100); await interfaces.chroma.setKeyRow(matrix); dbus.disconnect(); } 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();