1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 cfg = config.services.hardware.lcd;
10 pkg = lib.getBin pkgs.lcdproc;
11
12 serverCfg = pkgs.writeText "lcdd.conf" ''
13 [server]
14 DriverPath=${pkg}/lib/lcdproc/
15 ReportToSyslog=false
16 Bind=${cfg.serverHost}
17 Port=${toString cfg.serverPort}
18 ${cfg.server.extraConfig}
19 '';
20
21 clientCfg = pkgs.writeText "lcdproc.conf" ''
22 [lcdproc]
23 Server=${cfg.serverHost}
24 Port=${toString cfg.serverPort}
25 ReportToSyslog=false
26 ${cfg.client.extraConfig}
27 '';
28
29 serviceCfg = {
30 DynamicUser = true;
31 Restart = "on-failure";
32 Slice = "lcd.slice";
33 };
34
35in
36with lib;
37{
38
39 meta.maintainers = with maintainers; [ peterhoeg ];
40
41 options = with types; {
42 services.hardware.lcd = {
43 serverHost = mkOption {
44 type = str;
45 default = "localhost";
46 description = "Host on which LCDd is listening.";
47 };
48
49 serverPort = mkOption {
50 type = int;
51 default = 13666;
52 description = "Port on which LCDd is listening.";
53 };
54
55 server = {
56 enable = mkOption {
57 type = bool;
58 default = false;
59 description = "Enable the LCD panel server (LCDd)";
60 };
61
62 openPorts = mkOption {
63 type = bool;
64 default = false;
65 description = "Open the ports in the firewall";
66 };
67
68 usbPermissions = mkOption {
69 type = bool;
70 default = false;
71 description = ''
72 Set group-write permissions on a USB device.
73
74 A USB connected LCD panel will most likely require having its
75 permissions modified for lcdd to write to it. Enabling this option
76 sets group-write permissions on the device identified by
77 {option}`services.hardware.lcd.usbVid` and
78 {option}`services.hardware.lcd.usbPid`. In order to find the
79 values, you can run the {command}`lsusb` command. Example
80 output:
81
82 ```
83 Bus 005 Device 002: ID 0403:c630 Future Technology Devices International, Ltd lcd2usb interface
84 ```
85
86 In this case the vendor id is 0403 and the product id is c630.
87 '';
88 };
89
90 usbVid = mkOption {
91 type = str;
92 default = "";
93 description = "The vendor ID of the USB device to claim.";
94 };
95
96 usbPid = mkOption {
97 type = str;
98 default = "";
99 description = "The product ID of the USB device to claim.";
100 };
101
102 usbGroup = mkOption {
103 type = str;
104 default = "dialout";
105 description = "The group to use for settings permissions. This group must exist or you will have to create it.";
106 };
107
108 extraConfig = mkOption {
109 type = lines;
110 default = "";
111 description = "Additional configuration added verbatim to the server config.";
112 };
113 };
114
115 client = {
116 enable = mkOption {
117 type = bool;
118 default = false;
119 description = "Enable the LCD panel client (LCDproc)";
120 };
121
122 extraConfig = mkOption {
123 type = lines;
124 default = "";
125 description = "Additional configuration added verbatim to the client config.";
126 };
127
128 restartForever = mkOption {
129 type = bool;
130 default = true;
131 description = "Try restarting the client forever.";
132 };
133 };
134 };
135 };
136
137 config = mkIf (cfg.server.enable || cfg.client.enable) {
138 networking.firewall.allowedTCPPorts = mkIf (cfg.server.enable && cfg.server.openPorts) [
139 cfg.serverPort
140 ];
141
142 services.udev.extraRules = mkIf (cfg.server.enable && cfg.server.usbPermissions) ''
143 ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="${cfg.server.usbVid}", ATTRS{idProduct}=="${cfg.server.usbPid}", MODE="660", GROUP="${cfg.server.usbGroup}"
144 '';
145
146 systemd.services = {
147 lcdd = mkIf cfg.server.enable {
148 description = "LCDproc - server";
149 wantedBy = [ "lcd.target" ];
150 serviceConfig = serviceCfg // {
151 ExecStart = "${pkg}/bin/LCDd -f -c ${serverCfg}";
152 SupplementaryGroups = cfg.server.usbGroup;
153 };
154 };
155
156 lcdproc = mkIf cfg.client.enable {
157 description = "LCDproc - client";
158 after = [ "lcdd.service" ];
159 wantedBy = [ "lcd.target" ];
160 # Allow restarting for eternity
161 startLimitIntervalSec = lib.mkIf cfg.client.restartForever 0;
162 serviceConfig = serviceCfg // {
163 ExecStart = "${pkg}/bin/lcdproc -f -c ${clientCfg}";
164 # If the server is being restarted at the same time, the client will
165 # fail as it cannot connect, so space it out a bit.
166 RestartSec = "5";
167 };
168 };
169 };
170
171 systemd.targets.lcd = {
172 description = "LCD client/server";
173 after = [
174 "lcdd.service"
175 "lcdproc.service"
176 ];
177 wantedBy = [ "multi-user.target" ];
178 };
179 };
180}