1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.hardware.openrazer;
7 kernelPackages = config.boot.kernelPackages;
8
9 toPyBoolStr = b: if b then "True" else "False";
10
11 daemonExe = "${pkgs.openrazer-daemon}/bin/openrazer-daemon --config ${daemonConfFile}";
12
13 daemonConfFile = pkgs.writeTextFile {
14 name = "razer.conf";
15 text = ''
16 [General]
17 verbose_logging = ${toPyBoolStr cfg.verboseLogging}
18
19 [Startup]
20 sync_effects_enabled = ${toPyBoolStr cfg.syncEffectsEnabled}
21 devices_off_on_screensaver = ${toPyBoolStr cfg.devicesOffOnScreensaver}
22 battery_notifier = ${toPyBoolStr cfg.batteryNotifier.enable}
23 battery_notifier_freq = ${builtins.toString cfg.batteryNotifier.frequency}
24 battery_notifier_percent = ${builtins.toString cfg.batteryNotifier.percentage}
25
26 [Statistics]
27 key_statistics = ${toPyBoolStr cfg.keyStatistics}
28 '';
29 };
30
31 dbusServiceFile = pkgs.writeTextFile rec {
32 name = "org.razer.service";
33 destination = "/share/dbus-1/services/${name}";
34 text = ''
35 [D-BUS Service]
36 Name=org.razer
37 Exec=${daemonExe}
38 SystemdService=openrazer-daemon.service
39 '';
40 };
41
42 drivers = [
43 "razerkbd"
44 "razermouse"
45 "razerfirefly"
46 "razerkraken"
47 "razermug"
48 "razercore"
49 ];
50in
51{
52 options = {
53 hardware.openrazer = {
54 enable = mkEnableOption ''
55 OpenRazer drivers and userspace daemon
56 '';
57
58 verboseLogging = mkOption {
59 type = types.bool;
60 default = false;
61 description = ''
62 Whether to enable verbose logging. Logs debug messages.
63 '';
64 };
65
66 syncEffectsEnabled = mkOption {
67 type = types.bool;
68 default = true;
69 description = ''
70 Set the sync effects flag to true so any assignment of
71 effects will work across devices.
72 '';
73 };
74
75 devicesOffOnScreensaver = mkOption {
76 type = types.bool;
77 default = true;
78 description = ''
79 Turn off the devices when the systems screensaver kicks in.
80 '';
81 };
82
83 batteryNotifier = mkOption {
84 description = ''
85 Settings for device battery notifications.
86 '';
87 default = {};
88 type = types.submodule {
89 options = {
90 enable = mkOption {
91 type = types.bool;
92 default = true;
93 description = ''
94 Mouse battery notifier.
95 '';
96 };
97 frequency = mkOption {
98 type = types.int;
99 default = 600;
100 description = ''
101 How often battery notifications should be shown (in seconds).
102 A value of 0 disables notifications.
103 '';
104 };
105
106 percentage = mkOption {
107 type = types.int;
108 default = 33;
109 description = ''
110 At what battery percentage the device should reach before
111 sending notifications.
112 '';
113 };
114 };
115 };
116 };
117
118 keyStatistics = mkOption {
119 type = types.bool;
120 default = false;
121 description = ''
122 Collects number of keypresses per hour per key used to
123 generate a heatmap.
124 '';
125 };
126
127 users = mkOption {
128 type = with types; listOf str;
129 default = [];
130 description = ''
131 Usernames to be added to the "openrazer" group, so that they
132 can start and interact with the OpenRazer userspace daemon.
133 '';
134 };
135 };
136 };
137
138 imports = [
139 (mkRenamedOptionModule [ "hardware" "openrazer" "mouseBatteryNotifier" ] [ "hardware" "openrazer" "batteryNotifier" "enable" ])
140 ];
141
142 config = mkIf cfg.enable {
143 boot.extraModulePackages = [ kernelPackages.openrazer ];
144 boot.kernelModules = drivers;
145
146 # Makes the man pages available so you can successfully run
147 # > systemctl --user help openrazer-daemon
148 environment.systemPackages = [ pkgs.python3Packages.openrazer-daemon.man ];
149
150 services.udev.packages = [ kernelPackages.openrazer ];
151 services.dbus.packages = [ dbusServiceFile ];
152
153 # A user must be a member of the openrazer group in order to start
154 # the openrazer-daemon. Therefore we make sure that the group
155 # exists.
156 users.groups.openrazer = {
157 members = cfg.users;
158 };
159
160 systemd.user.services.openrazer-daemon = {
161 description = "Daemon to manage razer devices in userspace";
162 unitConfig.Documentation = "man:openrazer-daemon(8)";
163 # Requires a graphical session so the daemon knows when the screensaver
164 # starts. See the 'devicesOffOnScreensaver' option.
165 wantedBy = [ "graphical-session.target" ];
166 partOf = [ "graphical-session.target" ];
167 serviceConfig = {
168 Type = "dbus";
169 BusName = "org.razer";
170 ExecStart = "${daemonExe} --foreground";
171 Restart = "always";
172 };
173 };
174 };
175
176 meta = {
177 maintainers = with lib.maintainers; [ roelvandijk ];
178 };
179}