1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.octoprint;
8
9 baseConfig = {
10 plugins.curalegacy.cura_engine = "${pkgs.curaengine_stable}/bin/CuraEngine";
11 server.host = cfg.host;
12 server.port = cfg.port;
13 webcam.ffmpeg = "${pkgs.ffmpeg.bin}/bin/ffmpeg";
14 };
15
16 fullConfig = recursiveUpdate cfg.extraConfig baseConfig;
17
18 cfgUpdate = pkgs.writeText "octoprint-config.yaml" (builtins.toJSON fullConfig);
19
20 pluginsEnv = package.python.withPackages (ps: [ ps.octoprint ] ++ (cfg.plugins ps));
21
22 package = pkgs.octoprint;
23
24in
25{
26 ##### interface
27
28 options = {
29
30 services.octoprint = {
31
32 enable = mkEnableOption (lib.mdDoc "OctoPrint, web interface for 3D printers");
33
34 host = mkOption {
35 type = types.str;
36 default = "0.0.0.0";
37 description = lib.mdDoc ''
38 Host to bind OctoPrint to.
39 '';
40 };
41
42 port = mkOption {
43 type = types.port;
44 default = 5000;
45 description = lib.mdDoc ''
46 Port to bind OctoPrint to.
47 '';
48 };
49
50 openFirewall = mkOption {
51 type = types.bool;
52 default = false;
53 description = lib.mdDoc "Open ports in the firewall for OctoPrint.";
54 };
55
56 user = mkOption {
57 type = types.str;
58 default = "octoprint";
59 description = lib.mdDoc "User for the daemon.";
60 };
61
62 group = mkOption {
63 type = types.str;
64 default = "octoprint";
65 description = lib.mdDoc "Group for the daemon.";
66 };
67
68 stateDir = mkOption {
69 type = types.path;
70 default = "/var/lib/octoprint";
71 description = lib.mdDoc "State directory of the daemon.";
72 };
73
74 plugins = mkOption {
75 type = types.functionTo (types.listOf types.package);
76 default = plugins: [ ];
77 defaultText = literalExpression "plugins: []";
78 example = literalExpression "plugins: with plugins; [ themeify stlviewer ]";
79 description = lib.mdDoc "Additional plugins to be used. Available plugins are passed through the plugins input.";
80 };
81
82 extraConfig = mkOption {
83 type = types.attrs;
84 default = { };
85 description = lib.mdDoc "Extra options which are added to OctoPrint's YAML configuration file.";
86 };
87
88 };
89
90 };
91
92 ##### implementation
93
94 config = mkIf cfg.enable {
95
96 users.users = optionalAttrs (cfg.user == "octoprint") {
97 octoprint = {
98 group = cfg.group;
99 uid = config.ids.uids.octoprint;
100 };
101 };
102
103 users.groups = optionalAttrs (cfg.group == "octoprint") {
104 octoprint.gid = config.ids.gids.octoprint;
105 };
106
107 systemd.tmpfiles.rules = [
108 "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
109 # this will allow octoprint access to raspberry specific hardware to check for throttling
110 # read-only will not work: "VCHI initialization failed" error
111 "a /dev/vchiq - - - - u:octoprint:rw"
112 ];
113
114 systemd.services.octoprint = {
115 description = "OctoPrint, web interface for 3D printers";
116 wantedBy = [ "multi-user.target" ];
117 after = [ "network.target" ];
118 path = [ pluginsEnv ];
119
120 preStart = ''
121 if [ -e "${cfg.stateDir}/config.yaml" ]; then
122 ${pkgs.yaml-merge}/bin/yaml-merge "${cfg.stateDir}/config.yaml" "${cfgUpdate}" > "${cfg.stateDir}/config.yaml.tmp"
123 mv "${cfg.stateDir}/config.yaml.tmp" "${cfg.stateDir}/config.yaml"
124 else
125 cp "${cfgUpdate}" "${cfg.stateDir}/config.yaml"
126 chmod 600 "${cfg.stateDir}/config.yaml"
127 fi
128 '';
129
130 serviceConfig = {
131 ExecStart = "${pluginsEnv}/bin/octoprint serve -b ${cfg.stateDir}";
132 User = cfg.user;
133 Group = cfg.group;
134 SupplementaryGroups = [
135 "dialout"
136 ];
137 };
138 };
139
140 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
141 };
142}