Skip to content

Commit

Permalink
nixos/vudials: add module
Browse files Browse the repository at this point in the history
  • Loading branch information
bonds committed Oct 14, 2024
1 parent b3219e5 commit 6aa614e
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 0 deletions.
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2411.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@

## New Modules {#sec-release-24.11-new-modules}

- [vudials](https://vudials.com/), server for displaying CPU, GPU, and other metrics on VU1 e-ink displays. Available as [services.vudials](#opt-services.vudials.enable) service.

- [TaskChampion Sync-Server](https://github.com/GothenburgBitFactory/taskchampion-sync-server), a [Taskwarrior 3](https://taskwarrior.org/docs/upgrade-3/) sync server, replacing Taskwarrior 2's sync server named [`taskserver`](https://github.com/GothenburgBitFactory/taskserver).

- [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr), proxy server to bypass Cloudflare protection. Available as [services.flaresolverr](#opt-services.flaresolverr.enable) service.
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,7 @@
./services/monitoring/vmagent.nix
./services/monitoring/vmalert.nix
./services/monitoring/vnstat.nix
./services/monitoring/vudials.nix
./services/monitoring/watchdogd.nix
./services/monitoring/zabbix-agent.nix
./services/monitoring/zabbix-proxy.nix
Expand Down
132 changes: 132 additions & 0 deletions nixos/modules/services/monitoring/vudials.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
{
config,
lib,
pkgs,
...
}:

with lib;

let
cfg = config.services.vudials;
in
{
options.services.vudials = {
enable = mkEnableOption "VU Dials";

user = mkOption {
type = types.str;
default = "vudials";
description = "User account under which VU Dials runs.";
};

group = mkOption {
type = types.str;
default = "vudials";
description = "Group under which VU Dials runs.";
};

port = mkOption {
type = types.port;
default = 5340;
description = "Port on which VU Dials listens.";
};

cpudial = mkOption {
type = types.str;
default = "";
description = "UID of the dial that will display CPU load";
};

gpudial = mkOption {
type = types.str;
default = "";
description = "UID of the dial that will display GPU load";
};

memdial = mkOption {
type = types.str;
default = "";
description = "UID of the dial that will display memory load";
};

dskdial = mkOption {
type = types.str;
default = "";
description = "UID of the dial that will display % of the disk space used on root partition";
};

};

config = mkIf cfg.enable {
users.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
description = "VU Server user";
};

users.groups.${cfg.group} = { };

services.udev.extraRules = ''
ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", ATTRS{serial}=="DQ0164KM", SYMLINK+="vuserver-$attr{serial}", TAG+="systemd", ENV{SYSTEMD_WANTS}="vuserver@$attr{serial}.service", MODE="0666"
ACTION=="remove", SUBSYSTEM=="tty", ENV{ID_VENDOR_ID}=="0403", ENV{ID_MODEL_ID}=="6015", ENV{ID_SERIAL_SHORT}=="DQ0164KM", RUN+="${pkgs.systemd}/bin/systemctl stop vuserver@$env{ID_SERIAL_SHORT}.service"
'';

systemd.services."vuserver@" = {
description = "VU Server for %I. Provides API, admin web page, and pushed updates to USB dials";
partOf = [ "vuserver.target" ];

serviceConfig = {
ExecStart = "${pkgs.vuserver}/bin/vuserver";
User = cfg.user;
Group = cfg.group;
Restart = "on-failure";
WorkingDirectory = "${pkgs.vuserver}/lib";
RuntimeDirectory = "vuserver";
LogsDirectory = "vuserver";
StateDirectory = "vuserver";
TimeoutStopSec = "1s";

Environment = [
"STATEDIR=%S/vuserver"
"LOGSDIR=%L/vuserver"
"RUNTIMEDIR=%t/vuserver"
"DEVICE=/dev/vuserver-%I"
"PORT=${toString cfg.port}"
];
};
};

systemd.targets.vuserver = { };

systemd.services.vuclient = {
enable = true;
description = "Monitor computer and push info to VU server.";
wantedBy = [ "multi-user.target" ];
wants = [ "vuserver.target" ];
after = [ "vuserver.target" ];
serviceConfig = {
ExecStart = "${pkgs.vuclient}/bin/vuclient";
TimeoutStopSec = "5s";
Restart = "on-failure";
Environment = [
"CPUDIAL=${cfg.cpudial}"
"GPUDIAL=${cfg.gpudial}"
"MEMDIAL=${cfg.memdial}"
"DSKDIAL=${cfg.dskdial}"
];
};
};

powerManagement.powerDownCommands = lib.mkAfter ''
systemctl stop vuclient.service
sleep 1
'';

powerManagement.powerUpCommands = lib.mkAfter ''
systemctl start vuclient.service
'';

environment.systemPackages = [ pkgs.vuserver ];
};
}
31 changes: 31 additions & 0 deletions pkgs/by-name/vu/vuclient/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
lib,
stdenv,
fetchFromGitHub,
}:

stdenv.mkDerivation rec {
pname = "vuclient";
version = "1.0";

src = fetchFromGitHub {
owner = "bonds";
repo = "vuclient";
rev = "v${version}";
hash = "sha256-rmbGfj8Sqb/LpdfofmGUUtv5kr46DEM8imYNB5WjJdw=";
};

installPhase = ''
mkdir -p $out/bin
cp $src/vuclient $out/bin/vuclient
chmod +x $out/bin/vuclient
'';

meta = with lib; {
description = "A script that samples perf stats and sends them to a vuserver driving VU1 dials.";
homepage = "https://github.com/bonds/vuclient/";
license = licenses.mit;
platforms = platforms.linux;
maintainers = with maintainers; [ bonds ];
};
}
85 changes: 85 additions & 0 deletions pkgs/by-name/vu/vuserver/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
lib,
python3Packages,
fetchFromGitHub,
makeWrapper,
}:

python3Packages.buildPythonApplication rec {
pname = "vuserver";
version = "20240329";

src = fetchFromGitHub {
owner = "SasaKaranovic";
repo = "VU-Server";
rev = "v${version}";
sha256 = "sha256-K2oJrqgNGRus5bvYHdhtyDQeHvCbW+fAlQLMn00Ovco=";
};

format = "other";

nativeBuildInputs = [ makeWrapper ];

propagatedBuildInputs = with python3Packages; [
tornado
numpy
pillow
requests
pyyaml
ruamel-yaml
pyserial
];

dontBuild = true;

patchPhase = ''
substituteInPlace dials/base_logger.py \
--replace "logFile = f'/home/{getpass.getuser()}/KaranovicResearch/vudials/server.log'" \
"logFile = os.path.join(os.environ.get('LOGSDIR'), 'vuserver.log')"
substituteInPlace server.py \
--replace "pid_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), file_name)" \
"pid_file = os.path.join(os.environ.get('RUNTIMEDIR'), 'pid')"
substituteInPlace server.py \
--replace "WEB_ROOT = os.path.join(BASEDIR_PATH, 'www')" \
"WEB_ROOT = os.path.join(os.environ.get('RUNTIMEDIR'), 'www')"
substituteInPlace database.py \
--replace "database_path = os.path.join(os.path.dirname(__file__))" \
"database_path = os.environ.get('STATEDIR')"
substituteInPlace server_config.py \
--replace "'port': 3000" \
"'port': os.environ.get('PORT')"
substituteInPlace server_config.py \
--replace "'master_key': 'cTpAWYuRpA2zx75Yh961Cg'" \
"'master_key': os.environ.get('KEY')"
'';

installPhase = ''
mkdir -p "$out/lib"
cp -r * "$out/lib"
rm "$out/lib/config.yaml"
makeWrapper \
${python3Packages.python.interpreter} \
$out/bin/vuserver \
--run "cp -r $out/lib/www /run/vuserver/www" \
--run "chown -R vudials:vudials /run/vuserver/www" \
--run "chmod 775 /run/vuserver/www/assets/js/" \
--run "export KEY=\$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 64)" \
--run "echo \$KEY > /run/vuserver/key" \
--run "echo \"const API_MASTER_KEY = '\$KEY';\" > /run/vuserver/www/assets/js/vu1_gui_root.js.new" \
--run "sed 1d /run/vuserver/www/assets/js/vu1_gui_root.js >> /run/vuserver/www/assets/js/vu1_gui_root.js.new" \
--run "mv /run/vuserver/www/assets/js/vu1_gui_root.js.new /run/vuserver/www/assets/js/vu1_gui_root.js" \
--chdir "$out/lib" \
--add-flags "$out/lib/server.py" \
--set PYTHONPATH "$PYTHONPATH:$out/lib" \
'';

doCheck = false;

meta = with lib; {
description = "VU Server for controlling VU dials";
homepage = "https://github.com/SasaKaranovic/VU-Server";
license = licenses.mit;
maintainers = with maintainers; [ bonds ];
};
}

0 comments on commit 6aa614e

Please sign in to comment.