feat(octoprint): motion and notifs

This commit is contained in:
arcnmx 2024-07-14 18:44:05 -07:00
parent 6bf729ffe9
commit 7fe6d48ff0
11 changed files with 263 additions and 47 deletions

34
nixos/cameras/kitchen.nix Normal file
View file

@ -0,0 +1,34 @@
{
config,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mapDefaults;
inherit (config.services) motion;
in {
services.motion.cameras.kitchencam.settings = mapDefaults {
videodevice = "/dev/kitchencam";
v4l2_palette = 8;
width = 640;
height = 480;
framerate = 5;
camera_id = 1;
text_left = "kitchen";
};
services.udev.extraRules = let
inherit (lib.strings) concatStringsSep;
rules = [
''SUBSYSTEM=="video4linux"''
''ACTION=="add"''
''ATTR{index}=="0"''
''ATTRS{idProduct}=="2a25"''
''ATTRS{idVendor}=="1224"''
''SYMLINK+="kitchencam"''
''OWNER="${motion.user}"''
''TAG+="systemd"''
''ENV{SYSTEMD_WANTS}="motion.service"''
];
rulesLine = concatStringsSep ", " rules;
in rulesLine;
}

View file

@ -0,0 +1,12 @@
{
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapDefaults;
in {
services.motion.cameras.webcam.settings = mapDefaults {
videodevice = "/dev/video0";
camera_id = 3;
text_left = "logistics";
};
}

34
nixos/cameras/printer.nix Normal file
View file

@ -0,0 +1,34 @@
{
config,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mapDefaults;
inherit (config.services) motion;
in {
services.motion.cameras.printercam.settings = mapDefaults {
videodevice = "/dev/printercam";
width = 640;
height = 480;
framerate = 5;
camera_id = 2;
text_left = "";
#text_right = "";
};
services.udev.extraRules = let
inherit (lib.strings) concatStringsSep;
rules = [
''SUBSYSTEM=="video4linux"''
''ACTION=="add"''
''ATTR{index}=="0"''
''ATTRS{idProduct}=="6366"''
''ATTRS{idVendor}=="0c45"''
''SYMLINK+="printercam"''
''OWNER="${motion.user}"''
''TAG+="systemd"''
''ENV{SYSTEMD_WANTS}="motion.service"''
];
rulesLine = concatStringsSep ", " rules;
in rulesLine;
}

View file

@ -33,32 +33,7 @@ in {
webcontrol_port = webPort; webcontrol_port = webPort;
stream_port = streamPort; stream_port = streamPort;
}; };
cameras.kitchencam.settings = mapDefaults {
videodevice = "/dev/kitchencam";
v4l2_palette = 8;
width = 640;
height = 480;
framerate = 5;
camera_id = 1;
text_left = "kitchen";
};
}; };
services.udev.extraRules = let
inherit (lib.strings) concatStringsSep;
rules = [
''SUBSYSTEM=="video4linux"''
''ACTION=="add"''
''ATTR{index}=="0"''
''ATTRS{idProduct}=="2a25"''
''ATTRS{idVendor}=="1224"''
''SYMLINK+="kitchencam"''
''OWNER="${cfg.user}"''
''TAG+="systemd"''
''ENV{SYSTEMD_WANTS}="motion.service"''
];
rulesLine = concatStringsSep ", " rules;
in
mkIf cfg.enable rulesLine;
networking.firewall.interfaces.local = mkIf cfg.enable { networking.firewall.interfaces.local = mkIf cfg.enable {
allowedTCPPorts = [cfg.settings.stream_port cfg.settings.webcontrol_port]; allowedTCPPorts = [cfg.settings.stream_port cfg.settings.webcontrol_port];
}; };

View file

@ -1,14 +1,16 @@
{ {
pkgs,
config, config,
access, access,
gensokyo-zone,
lib, lib,
... ...
}: let }: let
inherit (gensokyo-zone.lib) domain;
inherit (lib.modules) mkIf mkMerge mkDefault; inherit (lib.modules) mkIf mkMerge mkDefault;
inherit (config.services) motion; inherit (config.services) motion;
cfg = config.services.octoprint; cfg = config.services.octoprint;
behindVouch = false; vouchHeader = null;
#vouchHeader = "X-Vouch-User";
in { in {
services.octoprint = { services.octoprint = {
enable = mkDefault true; enable = mkDefault true;
@ -16,6 +18,7 @@ in {
plugins = python3Packages: with python3Packages; [ plugins = python3Packages: with python3Packages; [
prometheus-exporter prometheus-exporter
octorant octorant
queue
abl-expert abl-expert
bedlevelvisualizer bedlevelvisualizer
#displayprogress / displaylayerprogress? #displayprogress / displaylayerprogress?
@ -66,27 +69,113 @@ in {
serial = { serial = {
port = "/dev/ttyUSB0"; port = "/dev/ttyUSB0";
baudrate = 115200; baudrate = 115200;
#autoconnect = true; autoconnect = true;
};
}
{
plugins.octorant = let
media = {
none = "none";
webcam = "snapshot";
#timelapse = ?;
};
in {
_config_version = 2;
events = {
printer_state_error.media = media.none;
printer_state_operational = {
enabled = false;
media = media.none;
};
printer_state_unknown.media = media.none;
printing_started = {
message = "New print started: **{name}**";
};
printing_cancelled = {
message = "Print cancelled after {time_formatted}";
};
printing_paused = {
message = "Print paused";
media = media.none;
};
printing_failed.message = "Print failed! :<";
printing_progress.message = "Printed **{progress}%** with {remaining_formatted} remaining";
printing_resumed = {
message = "Print resumed";
media = media.none;
};
shutdown = {
#enabled = false;
media = media.none;
};
startup = {
#enabled = false;
media = media.none;
};
timelapse_done = {
enabled = true;
# TODO: movie_basename needs uri encoding if it contains spaces .-.
message = "Timelapse for {gcode}: https://print.${domain}/downloads/timelapse/{movie_basename}";
media = media.none;
};
timelapse_failed.media = media.none;
transfer_done.media = media.none;
transfer_failed.media = media.none;
transfer_progress.media = media.none;
progress = {
#percentage_enabled = false;
percentage_step = "14";
throttle_enabled = true;
time_enabled = true;
throttle_step = "540";
time_step = "600";
};
};
# TODO: url = "https://discord.com/api/webhooks/etc";
}; };
} }
(mkIf motion.enable { (mkIf motion.enable {
webcam = { webcam = {
# TODO bitrate = "6000k";
ffmpegThreads = 2;
timelapse = {
fps = 25;
options.interval = 3;
postRoll = 0;
type = "timed";
};
};
plugins = {
classicwebcam = let
inherit (motion.cameras) printercam;
inherit (printercam.settings) camera_id;
in {
_config_version = 1;
snapshot = "https://kitchen.local.${domain}/${toString camera_id}/current";
stream = "https://kitchen.local.${domain}/${toString camera_id}/stream";
streamRatio = "4:3";
};
}; };
}) })
(mkIf (!behindVouch) { (mkIf (vouchHeader == null) {
accessControl = { accessControl = {
autologinLocal = true; autologinLocal = true;
autologinHeadsupAcknowledged = true;
#autologinAs = "guest"; #autologinAs = "guest";
autologinAs = "admin"; autologinAs = "admin";
localNetworks = access.cidrForNetwork.allLocal.all; localNetworks = access.cidrForNetwork.allLocal.all
++ [
# vouch protects it from the outside world so...
"0.0.0.0/0"
"::/0"
];
}; };
}) })
(mkIf behindVouch { (mkIf (vouchHeader != null) {
accessControl = { accessControl = {
trustRemoteUser = true; trustRemoteUser = true;
addRemoteUsers = true; addRemoteUsers = true;
remoteUserHeader = "X-Vouch-User"; remoteUserHeader = vouchHeader;
}; };
}) })
]; ];

View file

@ -11,7 +11,7 @@ in {
inherit (python3Packages) buildPlugin; inherit (python3Packages) buildPlugin;
}; };
packageOverrides = python3Packages: python3Packages'prev: lib.mapAttrs (mapPlugin python3Packages) { packageOverrides = python3Packages: python3Packages'prev: lib.mapAttrs (mapPlugin python3Packages) {
inherit (final.octoprintPlugins) prometheus-exporter octorant; inherit (final.octoprintPlugins) prometheus-exporter octorant queue printtimegenius;
}; };
octoprint = prev.octoprint.override (old: { octoprint = prev.octoprint.override (old: {
packageOverrides = lib.composeExtensions old.packageOverrides or (_: _: {}) packageOverrides; packageOverrides = lib.composeExtensions old.packageOverrides or (_: _: {}) packageOverrides;
@ -31,8 +31,20 @@ in {
prometheus-exporter = callPackage ../packages/octoprint/prometheus-exporter.nix { }; prometheus-exporter = callPackage ../packages/octoprint/prometheus-exporter.nix { };
octorant = callPackage ../packages/octoprint/octorant.nix { }; octorant = callPackage ../packages/octoprint/octorant.nix { };
queue = callPackage ../packages/octoprint/queue.nix { };
printtimegenius = let
printtimegenius = { fetchFromGitHub, python3Packages, buildPlugin }: octoprintPlugins.printtimegenius.overrideAttrs (old: rec {
version = lib.warnIf (lib.versionAtLeast old.version "2.3.2") "printtimegenius updated upstream" "2.3.3";
src = fetchFromGitHub {
inherit (old.src) owner repo;
rev = version;
sha256 = "sha256-hqm8RShCNpsVbrVXquat5VXqcVc7q5tn5+7Ipqmaw4U=";
};
});
in callPackage printtimegenius { };
}; };
# XXX: build broken upstream ugh...
curaengine = prev.curaengine.override { curaengine = prev.curaengine.override {
inherit (final.python311Packages) libarcus; inherit (final.python311Packages) libarcus;
}; };

View file

@ -2,14 +2,14 @@
, python3Packages , python3Packages
, fetchFromGitHub , fetchFromGitHub
}: let }: let
pname = "OctoPrint-Octorant";
version = "1.3.4"; version = "1.3.4";
in buildPlugin { in buildPlugin {
pname = "OctoPrint-Octorant"; inherit pname version;
inherit version;
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "bchanudet"; owner = "bchanudet";
repo = "OctoPrint-Octorant"; repo = pname;
rev = version; rev = version;
sha256 = "sha256-gP79zlJ8gdtpddXOJIMhouSbwXnrSf+c1bURkN/7jvw="; sha256 = "sha256-gP79zlJ8gdtpddXOJIMhouSbwXnrSf+c1bURkN/7jvw=";
}; };

View file

@ -0,0 +1,39 @@
diff --git a/octoprint_prometheus_exporter/__init__.py b/octoprint_prometheus_exporter/__init__.py
--- a/octoprint_prometheus_exporter/__init__.py
+++ b/octoprint_prometheus_exporter/__init__.py
@@ -53,15 +53,22 @@ class PrometheusExporterPlugin(octoprint.plugin.BlueprintPlugin,
self.print_completion_timer = None
def print_deregister_callback(self, label):
- if label != '':
+ self.print_progress_label = ''
+ if label == '':
+ return
+ try:
self.metrics.print_progress.remove(label)
self.metrics.print_time_elapsed.remove(label)
self.metrics.print_time_est.remove(label)
self.metrics.print_time_left_est.remove(label)
- self.print_progress_label = ''
+ except Exception as err:
+ self._logger.warning(err)
def slice_deregister_callback(self, label):
- self.metrics.slice_progress.remove(label)
+ try:
+ self.metrics.slice_progress.remove(label)
+ except Exception as err:
+ self._logger.warning(err)
def print_complete(self):
self.metrics.printing_time_total.inc(time.time() - self.print_time_start)
@@ -74,7 +81,8 @@ class PrometheusExporterPlugin(octoprint.plugin.BlueprintPlugin,
self.print_completion_timer = Timer(30, self.print_complete_callback)
self.print_completion_timer.start()
- Timer(30, lambda: self.print_deregister_callback(self.print_progress_label)).start()
+ print_progress_label = self.print_progress_label
+ Timer(30, lambda: self.print_deregister_callback(print_progress_label)).start()
def deactivateMetricsIfOffline(self, payload):
if payload['state_id'] == 'OFFLINE':

View file

@ -18,6 +18,10 @@ in buildPlugin {
prometheus-client prometheus-client
]; ];
patches = [
./prometheus-exporter-deregister.patch
];
meta = { meta = {
homepage = "https://github.com/tg44/OctoPrint-Prometheus-Exporter"; homepage = "https://github.com/tg44/OctoPrint-Prometheus-Exporter";
}; };

View file

@ -0,0 +1,23 @@
{ buildPlugin
, python3Packages
, fetchFromGitHub
}: let
version = "2.0.0";
pname = "OctoPrint-Queue";
in buildPlugin {
inherit pname version;
src = fetchFromGitHub {
owner = "chennes";
repo = pname;
rev = "v${version}";
sha256 = "sha256-uAG6GrUKXUdUTtzmjKWPiHxMa3ekvoLpSIvFMiJI+/8=";
};
propagatedBuildInputs = with python3Packages; [
];
meta = {
homepage = "https://github.com/chennes/OctoPrint-Queue";
};
}

View file

@ -13,7 +13,10 @@ in {
nixos.sops nixos.sops
nixos.base nixos.base
nixos.barcodebuddy-scanner nixos.barcodebuddy-scanner
nixos.kitchencam nixos.motion
nixos.cameras.kitchen
nixos.cameras.printer
nixos.cameras.logistics-webcam
nixos.octoprint nixos.octoprint
./hardware-configuration.nix ./hardware-configuration.nix
]; ];
@ -49,15 +52,6 @@ in {
#jack.enable = true; #jack.enable = true;
}; };
services.motion.cameras.webcam = {
#enable = false;
settings = {
videodevice = "/dev/video0";
camera_id = 2;
text_left = "logistics";
};
};
environment.systemPackages = [ pkgs.cura-octoprint ]; environment.systemPackages = [ pkgs.cura-octoprint ];
users.users.logistics = { users.users.logistics = {