mirror of
https://github.com/aminnairi/dotfiles.git
synced 2026-09-14 16:52:12 +02:00
546 lines
12 KiB
JavaScript
546 lines
12 KiB
JavaScript
import { URL } from "url";
|
|
import { request } from "https";
|
|
import { readFile } from "fs/promises";
|
|
import { cpus as cpuInfo, homedir } from "os";
|
|
import { exec } from "child_process";
|
|
import { createInterface } from "readline/promises";
|
|
import { stdin as input, stdout as output } from "process";
|
|
import { createRenderer } from "@aminnairi/i3status";
|
|
import { join } from "path";
|
|
|
|
// SETTINGS
|
|
|
|
const settings = await readFile(join(homedir(), ".config/i3/i3status/settings.json")).then(fileContent => {
|
|
return JSON.parse(fileContent);
|
|
}).catch(() => {
|
|
return {
|
|
weather: {
|
|
appid: ""
|
|
}
|
|
};
|
|
});
|
|
|
|
// HELPERS
|
|
|
|
const json = {
|
|
get: (url) => {
|
|
return new Promise((resolve, reject) => {
|
|
const { hostname, pathname, search } = new URL(url);
|
|
const path = `${pathname}${search}`;
|
|
|
|
let data = "";
|
|
|
|
const req = request({
|
|
hostname,
|
|
path,
|
|
port: 443,
|
|
method: "GET"
|
|
}, response => {
|
|
response.on("data", chunk => {
|
|
data += chunk.toString();
|
|
});
|
|
|
|
response.on("end", () => {
|
|
try {
|
|
resolve(JSON.parse(data));
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on("error", error => {
|
|
reject(error);
|
|
});
|
|
|
|
req.end();
|
|
});
|
|
}
|
|
};
|
|
|
|
const execute = (command) => {
|
|
return new Promise((resolve, reject) => {
|
|
exec(command, (error, stdout, stderr) => {
|
|
if (error) {
|
|
return reject(error);
|
|
}
|
|
|
|
if (stderr) {
|
|
return reject(new Error(stderr));
|
|
}
|
|
|
|
resolve(stdout);
|
|
});
|
|
});
|
|
};
|
|
|
|
const ignore = () => { };
|
|
|
|
// BUSINESS
|
|
|
|
const getCov19 = async () => {
|
|
return json.get("https://coronavirusapifr.herokuapp.com/data/live/france").then(response => {
|
|
return ` ${Math.round(response[0].TO * 10000) / 100}%`;
|
|
});
|
|
};
|
|
|
|
const getClock = () => {
|
|
const date = new Date();
|
|
return ` ${date.toLocaleDateString("fr-FR")} ${date.toLocaleTimeString("fr-FR")}`;
|
|
};
|
|
|
|
const getBitcoinRate = async () => {
|
|
return json.get("https://api.coindesk.com/v1/bpi/currentprice.json").then(json => {
|
|
return ` ${json.bpi.USD.rate}`;
|
|
});
|
|
};
|
|
|
|
const getWeather = async () => {
|
|
return json.get(`https://api.openweathermap.org/data/2.5/weather?q=paris&appid=${settings.weather.appid}&units=metric`).then(json => {
|
|
return ` ${Math.round((Number(json.main.temp) || 0) * 10) / 10}°C ${json.weather[0].main}`;
|
|
});
|
|
};
|
|
|
|
const getCpus = () => {
|
|
const cpus = cpuInfo();
|
|
const count = cpus.length;
|
|
const speed = Math.round(cpus.reduce((clock, cpu) => clock + cpu.speed, 0) / count) / 1000;
|
|
|
|
return ` ${count} CPUs ${speed}GHz`;
|
|
};
|
|
|
|
const getBattery = async () => {
|
|
return Promise.all([
|
|
readFile("/sys/class/power_supply/BATT/capacity"),
|
|
readFile("/sys/class/power_supply/BATT/status")
|
|
]).then(([batteryBuffer, statusBuffer]) => {
|
|
const battery = batteryBuffer.toString().trim();
|
|
const state = statusBuffer.toString().trim();
|
|
|
|
if (state === "Discharging") {
|
|
const full_text = ` ${battery}%`;
|
|
|
|
if (battery <= 10) {
|
|
return {
|
|
color: "#FF0000",
|
|
full_text
|
|
};
|
|
}
|
|
|
|
if (battery <= 20) {
|
|
return {
|
|
color: "#FF7F00",
|
|
full_text
|
|
};
|
|
}
|
|
|
|
return {
|
|
color: "#FFFFFF",
|
|
full_text
|
|
};
|
|
}
|
|
|
|
return {
|
|
color: "#00ff00",
|
|
full_text: ` ${battery}%`
|
|
}
|
|
});
|
|
};
|
|
|
|
const getBrightness = async () => {
|
|
return execute("cat /sys/class/backlight/intel_backlight/brightness").then(brightness => {
|
|
const brightnessLevel = Math.round((Number(brightness.toString().trim()) || 0) / 1000);
|
|
|
|
return ` ${brightnessLevel}%`;
|
|
});
|
|
};
|
|
|
|
const getVolume = async () => {
|
|
return Promise.all([
|
|
execute("pactl get-sink-volume 0 | grep 'Volume' | cut -d '/' -f 2"),
|
|
execute("pactl get-sink-mute 0")
|
|
]).then(([volume, muted]) => {
|
|
return `${muted.includes("no") ? "" : ""} ${volume.trim()}`;
|
|
});
|
|
};
|
|
|
|
const getMemory = async () => {
|
|
return Promise.all([
|
|
execute("cat /proc/meminfo | grep MemTotal | cut -d ' ' -f 9"),
|
|
execute("cat /proc/meminfo | grep MemFree | cut -d ' ' -f 11")
|
|
]).then(([rawTotalMemory, rawFreeMemory]) => {
|
|
const totalMemory = Math.round((Number(rawTotalMemory.trim()) || 0) / 100000) / 10;
|
|
const freeMemory = Math.round((Number(rawFreeMemory.trim()) || 0) / 100000) / 10;
|
|
|
|
return ` ${freeMemory}Go / ${totalMemory}Go`;
|
|
});
|
|
};
|
|
|
|
const getDiskSpace = async () => {
|
|
return Promise.all([
|
|
execute("df -h | grep '/\$' | cut -d ' ' -f 7"),
|
|
execute("df -h | grep '/\$' | cut -d ' ' -f 3")
|
|
]).then(([rawDiskSpaceRemaining, rawDiskSpaceTotal]) => {
|
|
const diskSpaceRemaining = rawDiskSpaceRemaining.trim();
|
|
const diskSpaceTotal = rawDiskSpaceTotal.trim();
|
|
|
|
return ` ${diskSpaceRemaining} / ${diskSpaceTotal}`;
|
|
});
|
|
};
|
|
|
|
// SETUP
|
|
|
|
const render = createRenderer({
|
|
createInterface,
|
|
input,
|
|
output
|
|
});
|
|
|
|
const dispatch = render({
|
|
initialBlocks: [
|
|
{
|
|
name: "disk",
|
|
full_text: "",
|
|
separator: false
|
|
},
|
|
{
|
|
name: "memory",
|
|
full_text: "",
|
|
separator: false
|
|
},
|
|
{
|
|
name: "cpus",
|
|
full_text: getCpus(),
|
|
separator: false
|
|
},
|
|
{
|
|
name: "bitcoin",
|
|
full_text: "",
|
|
separator: false
|
|
},
|
|
{
|
|
name: "cov19",
|
|
full_text: "",
|
|
separator: false
|
|
},
|
|
{
|
|
name: "weather",
|
|
full_text: "",
|
|
separator: false
|
|
},
|
|
{
|
|
name: "battery",
|
|
full_text: "",
|
|
separator: false
|
|
},
|
|
{
|
|
name: "brightness",
|
|
full_text: "",
|
|
separator: false
|
|
},
|
|
{
|
|
name: "volume",
|
|
full_text: "",
|
|
separator: false
|
|
},
|
|
{
|
|
name: "clock",
|
|
full_text: getClock(),
|
|
separator: false
|
|
},
|
|
],
|
|
onEvent: ({ event: { name, button, modifiers }, dispatch }) => {
|
|
const leftClick = 1;
|
|
|
|
if (name === "clock" && button === leftClick && modifiers.length === 1 && modifiers.includes("Control")) {
|
|
return execute("librewolf https://calendar.google.com");
|
|
}
|
|
|
|
if (name === "weather" && button === 1 && modifiers.length === 0) {
|
|
return getWeather().then(weather => {
|
|
dispatch({
|
|
name: "WEATHER_SET_FULL_TEXT",
|
|
payload: weather
|
|
});
|
|
}).catch(ignore);
|
|
}
|
|
|
|
if (name === "bitcoin" && button === 1) {
|
|
if (modifiers.length === 0) {
|
|
return getBitcoinRate().then(rate => {
|
|
dispatch({
|
|
name: "BITCOIN_SET_FULL_TEXT",
|
|
payload: rate
|
|
});
|
|
}).catch(ignore);
|
|
}
|
|
|
|
if (modifiers.length === 1 && modifiers.includes("Control")) {
|
|
return execute("librewolf https://coindesk.com/price/bitcoin");
|
|
}
|
|
}
|
|
},
|
|
onDispatch: ({ blocks, action: { name, payload } }) => {
|
|
switch (name) {
|
|
case "CLOCK_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "clock") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
})
|
|
|
|
case "BITCOIN_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "bitcoin") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
});
|
|
|
|
case "WEATHER_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "weather") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
});
|
|
|
|
case "CPUS_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "cpus") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
});
|
|
|
|
case "BATTERY_SET_FULL_TEXT_COLOR":
|
|
return blocks.map(block => {
|
|
if (block.name !== "battery") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload.full_text,
|
|
color: payload.color
|
|
};
|
|
});
|
|
|
|
case "BRIGHTNESS_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "brightness") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
});
|
|
|
|
case "VOLUME_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "volume") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
});
|
|
|
|
case "MEMORY_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "memory") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
});
|
|
|
|
case "DISK_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "disk") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
});
|
|
|
|
case "COV19_SET_FULL_TEXT":
|
|
return blocks.map(block => {
|
|
if (block.name !== "cov19") {
|
|
return block;
|
|
}
|
|
|
|
return {
|
|
...block,
|
|
full_text: payload
|
|
};
|
|
});
|
|
|
|
default:
|
|
return blocks;
|
|
}
|
|
}
|
|
});
|
|
|
|
// INTERVALS
|
|
|
|
setInterval(() => {
|
|
dispatch({
|
|
name: "CLOCK_SET_FULL_TEXT",
|
|
payload: getClock()
|
|
});
|
|
|
|
dispatch({
|
|
name: "CPUS_SET_FULL_TEXT",
|
|
payload: getCpus()
|
|
});
|
|
|
|
getBattery().then(battery => {
|
|
dispatch({
|
|
name: "BATTERY_SET_FULL_TEXT_COLOR",
|
|
payload: battery
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getBrightness().then(brightness => {
|
|
dispatch({
|
|
name: "BRIGHTNESS_SET_FULL_TEXT",
|
|
payload: brightness
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getVolume().then(volume => {
|
|
dispatch({
|
|
name: "VOLUME_SET_FULL_TEXT",
|
|
payload: volume
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getMemory().then(memory => {
|
|
dispatch({
|
|
name: "MEMORY_SET_FULL_TEXT",
|
|
payload: memory
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getDiskSpace().then(diskSpace => {
|
|
dispatch({
|
|
name: "DISK_SET_FULL_TEXT",
|
|
payload: diskSpace
|
|
});
|
|
}).catch(ignore);
|
|
}, 1000);
|
|
|
|
setInterval(() => {
|
|
getBitcoinRate().then(rate => {
|
|
dispatch({
|
|
name: "BITCOIN_SET_FULL_TEXT",
|
|
payload: rate
|
|
});
|
|
}).catch(ignore);
|
|
}, 1000 * 60 * 10);
|
|
|
|
setInterval(() => {
|
|
getWeather().then(weather => {
|
|
dispatch({
|
|
name: "WEATHER_SET_FULL_TEXT",
|
|
payload: weather
|
|
});
|
|
}).catch(ignore);
|
|
|
|
}, 1000 * 60 * 30);
|
|
|
|
setInterval(() => {
|
|
getCov19().then(payload => {
|
|
dispatch({
|
|
name: "COV19_SET_FULL_TEXT",
|
|
payload
|
|
});
|
|
}).catch(ignore);
|
|
}, 1000 * 60 * 60 * 12);
|
|
|
|
// INITIAL SETUP
|
|
|
|
getBitcoinRate().then(rate => {
|
|
dispatch({
|
|
name: "BITCOIN_SET_FULL_TEXT",
|
|
payload: rate
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getWeather().then(weather => {
|
|
dispatch({
|
|
name: "WEATHER_SET_FULL_TEXT",
|
|
payload: weather
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getBattery().then(battery => {
|
|
dispatch({
|
|
name: "BATTERY_SET_FULL_TEXT_COLOR",
|
|
payload: battery
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getBrightness().then(brightness => {
|
|
dispatch({
|
|
name: "BRIGHTNESS_SET_FULL_TEXT",
|
|
payload: brightness
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getVolume().then(volume => {
|
|
dispatch({
|
|
name: "VOLUME_SET_FULL_TEXT",
|
|
payload: volume
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getMemory().then(memory => {
|
|
dispatch({
|
|
name: "MEMORY_SET_FULL_TEXT",
|
|
payload: memory
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getDiskSpace().then(diskSpace => {
|
|
dispatch({
|
|
name: "DISK_SET_FULL_TEXT",
|
|
payload: diskSpace
|
|
});
|
|
}).catch(ignore);
|
|
|
|
getCov19().then(payload => {
|
|
dispatch({
|
|
name: "COV19_SET_FULL_TEXT",
|
|
payload
|
|
});
|
|
}).catch(ignore);
|