1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7
8with lib;
9
10let
11 cfg = config.services.clight;
12
13 toConf =
14 v:
15 if builtins.isFloat v then
16 toString v
17 else if isInt v then
18 toString v
19 else if isBool v then
20 boolToString v
21 else if isString v then
22 ''"${escape [ ''"'' ] v}"''
23 else if isList v then
24 "[ " + concatMapStringsSep ", " toConf v + " ]"
25 else if isAttrs v then
26 "\n{\n" + convertAttrs v + "\n}"
27 else
28 abort "clight.toConf: unexpected type (v = ${v})";
29
30 getSep = v: if isAttrs v then ":" else "=";
31
32 convertAttrs =
33 attrs:
34 concatStringsSep "\n" (
35 mapAttrsToList (name: value: "${toString name} ${getSep value} ${toConf value};") attrs
36 );
37
38 clightConf = pkgs.writeText "clight.conf" (
39 convertAttrs (filterAttrs (_: value: value != null) cfg.settings)
40 );
41in
42{
43 options.services.clight = {
44 enable = mkEnableOption "clight";
45
46 temperature = {
47 day = mkOption {
48 type = types.int;
49 default = 5500;
50 description = ''
51 Colour temperature to use during the day, between
52 `1000` and `25000` K.
53 '';
54 };
55 night = mkOption {
56 type = types.int;
57 default = 3700;
58 description = ''
59 Colour temperature to use at night, between
60 `1000` and `25000` K.
61 '';
62 };
63 };
64
65 settings =
66 let
67 validConfigTypes =
68 with types;
69 oneOf [
70 int
71 str
72 bool
73 float
74 ];
75 collectionTypes =
76 with types;
77 oneOf [
78 validConfigTypes
79 (listOf validConfigTypes)
80 ];
81 in
82 mkOption {
83 type = with types; attrsOf (nullOr (either collectionTypes (attrsOf collectionTypes)));
84 default = { };
85 example = {
86 captures = 20;
87 gamma_long_transition = true;
88 ac_capture_timeouts = [
89 120
90 300
91 60
92 ];
93 };
94 description = ''
95 Additional configuration to extend clight.conf. See
96 <https://github.com/FedeDP/Clight/blob/master/Extra/clight.conf> for a
97 sample configuration file.
98 '';
99 };
100 };
101
102 config = mkIf cfg.enable {
103 assertions =
104 let
105 inRange =
106 v: l: r:
107 v >= l && v <= r;
108 in
109 [
110 {
111 assertion =
112 config.location.provider == "manual"
113 -> inRange config.location.latitude (-90) 90 && inRange config.location.longitude (-180) 180;
114 message = "You must specify a valid latitude and longitude if manually providing location";
115 }
116 ];
117
118 boot.kernelModules = [ "i2c_dev" ];
119 environment.systemPackages = with pkgs; [
120 clight
121 clightd
122 ];
123 services.dbus.packages = with pkgs; [
124 clight
125 clightd
126 ];
127 services.upower.enable = true;
128
129 services.clight.settings = {
130 gamma.temp =
131 with cfg.temperature;
132 mkDefault [
133 day
134 night
135 ];
136 }
137 // (optionalAttrs (config.location.provider == "manual") {
138 daytime.latitude = mkDefault config.location.latitude;
139 daytime.longitude = mkDefault config.location.longitude;
140 });
141
142 services.geoclue2.appConfig.clightc = {
143 isAllowed = true;
144 isSystem = true;
145 };
146
147 systemd.services.clightd = {
148 requires = [ "polkit.service" ];
149 wantedBy = [ "multi-user.target" ];
150
151 description = "Bus service to manage various screen related properties (gamma, dpms, backlight)";
152 serviceConfig = {
153 Type = "dbus";
154 BusName = "org.clightd.clightd";
155 Restart = "on-failure";
156 RestartSec = 5;
157 ExecStart = ''
158 ${pkgs.clightd}/bin/clightd
159 '';
160 };
161 };
162
163 systemd.user.services.clight = {
164 after = [
165 "upower.service"
166 "clightd.service"
167 ];
168 wants = [
169 "upower.service"
170 "clightd.service"
171 ];
172 partOf = [ "graphical-session.target" ];
173 wantedBy = [ "graphical-session.target" ];
174
175 description = "C daemon to adjust screen brightness to match ambient brightness, as computed capturing frames from webcam";
176 serviceConfig = {
177 Restart = "on-failure";
178 RestartSec = 5;
179 ExecStart = ''
180 ${pkgs.clight}/bin/clight --conf-file ${clightConf}
181 '';
182 };
183 };
184 };
185}