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