···409 (optionalString (cfg.config != null) copyConfig) +
410 (optionalString (cfg.lovelaceConfig != null) copyLovelaceConfig)
411 ;
0412 serviceConfig = let
413 # List of capabilities to equip home-assistant with, depending on configured components
414 capabilities = lib.unique ([
···409 (optionalString (cfg.config != null) copyConfig) +
410 (optionalString (cfg.lovelaceConfig != null) copyLovelaceConfig)
411 ;
412+ environment.PYTHONPATH = package.pythonPath;
413 serviceConfig = let
414 # List of capabilities to equip home-assistant with, depending on configured components
415 capabilities = lib.unique ([
+18-21
nixos/modules/services/misc/mbpfan.nix
···1{ config, lib, pkgs, ... }:
2-3with lib;
45let
···16 type = types.package;
17 default = pkgs.mbpfan;
18 defaultText = literalExpression "pkgs.mbpfan";
19- description = lib.mdDoc ''
20- The package used for the mbpfan daemon.
21- '';
22 };
2324 verbose = mkOption {
25 type = types.bool;
26 default = false;
27- description = lib.mdDoc ''
28- If true, sets the log level to verbose.
29- '';
000030 };
3132 settings = mkOption {
···35 type = types.submodule {
36 freeformType = settingsFormat.type;
3738- options.general.min_fan1_speed = mkOption {
39- type = types.nullOr types.int;
40- default = 2000;
41- description = lib.mdDoc ''
42- You can check minimum and maximum fan limits with
43- `cat /sys/devices/platform/applesmc.768/fan*_min` and
44- `cat /sys/devices/platform/applesmc.768/fan*_max` respectively.
45- Setting to null implies using default value from applesmc.
46- '';
47- };
48 options.general.low_temp = mkOption {
49 type = types.int;
50- default = 55;
51 description = lib.mdDoc "If temperature is below this, fans will run at minimum speed.";
52 };
53 options.general.high_temp = mkOption {
54 type = types.int;
55- default = 58;
56 description = lib.mdDoc "If temperature is above this, fan speed will gradually increase.";
57 };
58 options.general.max_temp = mkOption {
···79 ];
8081 config = mkIf cfg.enable {
82- boot.kernelModules = [ "coretemp" "applesmc" ];
000008384- environment.etc."mbpfan.conf".source = settingsFile;
85 environment.systemPackages = [ cfg.package ];
08687 systemd.services.mbpfan = {
88 description = "A fan manager daemon for MacBook Pro";
···1{ config, lib, pkgs, ... }:
02with lib;
34let
···15 type = types.package;
16 default = pkgs.mbpfan;
17 defaultText = literalExpression "pkgs.mbpfan";
18+ description = lib.mdDoc "The package used for the mbpfan daemon.";
0019 };
2021 verbose = mkOption {
22 type = types.bool;
23 default = false;
24+ description = lib.mdDoc "If true, sets the log level to verbose.";
25+ };
26+27+ aggressive = mkOption {
28+ type = types.bool;
29+ default = false;
30+ description = lib.mdDoc "If true, favors higher default fan speeds.";
31 };
3233 settings = mkOption {
···36 type = types.submodule {
37 freeformType = settingsFormat.type;
38000000000039 options.general.low_temp = mkOption {
40 type = types.int;
41+ default = 63;
42 description = lib.mdDoc "If temperature is below this, fans will run at minimum speed.";
43 };
44 options.general.high_temp = mkOption {
45 type = types.int;
46+ default = 66;
47 description = lib.mdDoc "If temperature is above this, fan speed will gradually increase.";
48 };
49 options.general.max_temp = mkOption {
···70 ];
7172 config = mkIf cfg.enable {
73+ services.mbpfan.settings = mkIf cfg.aggressive {
74+ general.min_fan1_speed = mkDefault 2000;
75+ general.low_temp = mkDefault 55;
76+ general.high_temp = mkDefault 58;
77+ general.max_temp = mkDefault 70;
78+ };
7980+ boot.kernelModules = [ "coretemp" "applesmc" ];
81 environment.systemPackages = [ cfg.package ];
82+ environment.etc."mbpfan.conf".source = settingsFile;
8384 systemd.services.mbpfan = {
85 description = "A fan manager daemon for MacBook Pro";
+57-48
nixos/tests/home-assistant.nix
···22 enable = true;
23 inherit configDir;
2425- # tests loading components by overriding the package
26 package = (pkgs.home-assistant.override {
27 extraPackages = ps: with ps; [
28 colorama
29 ];
30- extraComponents = [ "zha" ];
31- }).overrideAttrs (oldAttrs: {
32- doInstallCheck = false;
033 });
3435- # tests loading components from the module
36 extraComponents = [
37- "wake_on_lan"
38 ];
3940- # test extra package passing from the module
41 extraPackages = python3Packages: with python3Packages; [
42 psycopg2
43 ];
···111 };
112113 testScript = { nodes, ... }: let
114- system = nodes.hass.config.system.build.toplevel;
115 in
116 ''
117- import re
118 import json
119120 start_all()
121122- # Parse the package path out of the systemd unit, as we cannot
123- # access the final package, that is overridden inside the module,
124- # by any other means.
125- pattern = re.compile(r"path=(?P<path>[\/a-z0-9-.]+)\/bin\/hass")
126- response = hass.execute("systemctl show -p ExecStart home-assistant.service")[1]
127- match = pattern.search(response)
128- assert match
129- package = match.group('path')
130131-132- def get_journal_cursor(host) -> str:
133- exit, out = host.execute("journalctl -u home-assistant.service -n1 -o json-pretty --output-fields=__CURSOR")
134 assert exit == 0
135 return json.loads(out)["__CURSOR"]
136137138- def wait_for_homeassistant(host, cursor):
139- host.wait_until_succeeds(f"journalctl --after-cursor='{cursor}' -u home-assistant.service | grep -q 'Home Assistant initialized in'")
000000000000140141142 hass.wait_for_unit("home-assistant.service")
143- cursor = get_journal_cursor(hass)
144145 with subtest("Check that YAML configuration file is in place"):
146 hass.succeed("test -L ${configDir}/configuration.yaml")
···148 with subtest("Check the lovelace config is copied because lovelaceConfigWritable = true"):
149 hass.succeed("test -f ${configDir}/ui-lovelace.yaml")
150151- with subtest("Check extraComponents and extraPackages are considered from the package"):
152- hass.succeed(f"grep -q 'colorama' {package}/extra_packages")
153- hass.succeed(f"grep -q 'zha' {package}/extra_components")
154-155- with subtest("Check extraComponents and extraPackages are considered from the module"):
156- hass.succeed(f"grep -q 'psycopg2' {package}/extra_packages")
157- hass.succeed(f"grep -q 'wake_on_lan' {package}/extra_components")
158-159 with subtest("Check that Home Assistant's web interface and API can be reached"):
160- wait_for_homeassistant(hass, cursor)
161 hass.wait_for_open_port(8123)
162 hass.succeed("curl --fail http://localhost:8123/lovelace")
16300000000000164 with subtest("Check that capabilities are passed for emulated_hue to bind to port 80"):
165 hass.wait_for_open_port(80)
166 hass.succeed("curl --fail http://localhost:80/description.xml")
···169 hass.succeed("systemctl show -p DeviceAllow home-assistant.service | grep -q char-ttyUSB")
170171 with subtest("Check service reloads when configuration changes"):
172- # store the old pid of the process
173- pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
174- cursor = get_journal_cursor(hass)
175- hass.succeed("${system}/specialisation/differentName/bin/switch-to-configuration test")
176- new_pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
177- assert pid == new_pid, "The PID of the process should not change between process reloads"
178- wait_for_homeassistant(hass, cursor)
179180- with subtest("check service restarts when package changes"):
181- pid = new_pid
182- cursor = get_journal_cursor(hass)
183- hass.succeed("${system}/specialisation/newFeature/bin/switch-to-configuration test")
184- new_pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
185- assert pid != new_pid, "The PID of the process shoudl change when the HA binary changes"
186- wait_for_homeassistant(hass, cursor)
00000187188 with subtest("Check that no errors were logged"):
189- output_log = hass.succeed("cat ${configDir}/home-assistant.log")
190- assert "ERROR" not in output_log
191192 with subtest("Check systemd unit hardening"):
193 hass.log(hass.succeed("systemctl cat home-assistant.service"))
···22 enable = true;
23 inherit configDir;
2425+ # provide dependencies through package overrides
26 package = (pkgs.home-assistant.override {
27 extraPackages = ps: with ps; [
28 colorama
29 ];
30+ extraComponents = [
31+ # test char-tty device allow propagation into the service
32+ "zha"
33+ ];
34 });
3536+ # provide component dependencies explicitly from the module
37 extraComponents = [
38+ "mqtt"
39 ];
4041+ # provide package for postgresql support
42 extraPackages = python3Packages: with python3Packages; [
43 psycopg2
44 ];
···112 };
113114 testScript = { nodes, ... }: let
115+ system = nodes.hass.system.build.toplevel;
116 in
117 ''
0118 import json
119120 start_all()
12100000000122123+ def get_journal_cursor() -> str:
124+ exit, out = hass.execute("journalctl -u home-assistant.service -n1 -o json-pretty --output-fields=__CURSOR")
0125 assert exit == 0
126 return json.loads(out)["__CURSOR"]
127128129+ def get_journal_since(cursor) -> str:
130+ exit, out = hass.execute(f"journalctl --after-cursor='{cursor}' -u home-assistant.service")
131+ assert exit == 0
132+ return out
133+134+135+ def get_unit_property(property) -> str:
136+ exit, out = hass.execute(f"systemctl show --property={property} home-assistant.service")
137+ assert exit == 0
138+ return out
139+140+141+ def wait_for_homeassistant(cursor):
142+ hass.wait_until_succeeds(f"journalctl --after-cursor='{cursor}' -u home-assistant.service | grep -q 'Home Assistant initialized in'")
143144145 hass.wait_for_unit("home-assistant.service")
146+ cursor = get_journal_cursor()
147148 with subtest("Check that YAML configuration file is in place"):
149 hass.succeed("test -L ${configDir}/configuration.yaml")
···151 with subtest("Check the lovelace config is copied because lovelaceConfigWritable = true"):
152 hass.succeed("test -f ${configDir}/ui-lovelace.yaml")
15300000000154 with subtest("Check that Home Assistant's web interface and API can be reached"):
155+ wait_for_homeassistant(cursor)
156 hass.wait_for_open_port(8123)
157 hass.succeed("curl --fail http://localhost:8123/lovelace")
158159+ with subtest("Check that optional dependencies are in the PYTHONPATH"):
160+ env = get_unit_property("Environment")
161+ python_path = env.split("PYTHONPATH=")[1].split()[0]
162+ for package in ["colorama", "paho-mqtt", "psycopg2"]:
163+ assert package in python_path, f"{package} not in PYTHONPATH"
164+165+ with subtest("Check that declaratively configured components get setup"):
166+ journal = get_journal_since(cursor)
167+ for domain in ["emulated_hue", "wake_on_lan"]:
168+ assert f"Setup of domain {domain} took" in journal, f"{domain} setup missing"
169+170 with subtest("Check that capabilities are passed for emulated_hue to bind to port 80"):
171 hass.wait_for_open_port(80)
172 hass.succeed("curl --fail http://localhost:80/description.xml")
···175 hass.succeed("systemctl show -p DeviceAllow home-assistant.service | grep -q char-ttyUSB")
176177 with subtest("Check service reloads when configuration changes"):
178+ pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
179+ cursor = get_journal_cursor()
180+ hass.succeed("${system}/specialisation/differentName/bin/switch-to-configuration test")
181+ new_pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
182+ assert pid == new_pid, "The PID of the process should not change between process reloads"
183+ wait_for_homeassistant(cursor)
0184185+ with subtest("Check service restarts when dependencies change"):
186+ pid = new_pid
187+ cursor = get_journal_cursor()
188+ hass.succeed("${system}/specialisation/newFeature/bin/switch-to-configuration test")
189+ new_pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
190+ assert pid != new_pid, "The PID of the process should change when its PYTHONPATH changess"
191+ wait_for_homeassistant(cursor)
192+193+ with subtest("Check that new components get setup after restart"):
194+ journal = get_journal_since(cursor)
195+ for domain in ["esphome"]:
196+ assert f"Setup of domain {domain} took" in journal, f"{domain} setup missing"
197198 with subtest("Check that no errors were logged"):
199+ hass.fail("journalctl -u home-assistant -o cat | grep -q ERROR")
0200201 with subtest("Check systemd unit hardening"):
202 hass.log(hass.succeed("systemctl cat home-assistant.service"))
···26, pulseaudioSupport ? mediaSupport
27, libpulseaudio
28, apulse
02930# Media support (implies audio support)
31, mediaSupport ? true
···57 libPath = lib.makeLibraryPath libPkgs;
5859 libPkgs = [
060 atk
61 cairo
62 dbus
···85 fteLibPath = lib.makeLibraryPath [ stdenv.cc.cc gmp ];
8687 # Upstream source
88- version = "11.5.8";
8990- lang = "en-US";
9192 srcs = {
93 x86_64-linux = fetchurl {
···97 "https://tor.eff.org/dist/torbrowser/${version}/tor-browser-linux64-${version}_${lang}.tar.xz"
98 "https://tor.calyxinstitute.org/dist/torbrowser/${version}/tor-browser-linux64-${version}_${lang}.tar.xz"
99 ];
100- sha256 = "sha256-/KK9oTijk5dEziAwp5966NaM2V4k1mtBjTJq88Ct7N0=";
101 };
102103 i686-linux = fetchurl {
···107 "https://tor.eff.org/dist/torbrowser/${version}/tor-browser-linux32-${version}_${lang}.tar.xz"
108 "https://tor.calyxinstitute.org/dist/torbrowser/${version}/tor-browser-linux32-${version}_${lang}.tar.xz"
109 ];
110- sha256 = "sha256-TGdJ5yIeo0YQ4XSsb9lv3vuW6qEjhFe7KBmkjYO6fAc=";
111 };
112 };
113in
···291 # TBB will fail if ownership is too permissive
292 chmod 0700 "\$HOME/TorBrowser/Data/Tor"
293294- # Initialize the browser profile state. Note that the only data
295- # copied from the Store payload is the initial bookmark file, which is
296- # never updated once created. All other files under user's profile
297- # dir are generated by TBB.
298 mkdir -p "\$HOME/TorBrowser/Data/Browser/profile.default"
299- cp -u --no-preserve=mode,owner "$TBB_IN_STORE/TorBrowser/Data/Browser/profile.default/bookmarks.html" \
300- "\$HOME/TorBrowser/Data/Browser/profile.default/bookmarks.html"
301302 # Clear some files if the last known store path is different from the new one
303 : "\''${KNOWN_STORE_PATH:=\$HOME/known-store-path}"
···324 # Font cache files capture store paths; clear them out on the off
325 # chance that TBB would continue using old font files.
326 rm -rf "\$HOME/.cache/fontconfig"
000327328 # Manually specify data paths (by default TB attempts to create these in the store)
329 {
···26, pulseaudioSupport ? mediaSupport
27, libpulseaudio
28, apulse
29+, alsa-lib
3031# Media support (implies audio support)
32, mediaSupport ? true
···58 libPath = lib.makeLibraryPath libPkgs;
5960 libPkgs = [
61+ alsa-lib
62 atk
63 cairo
64 dbus
···87 fteLibPath = lib.makeLibraryPath [ stdenv.cc.cc gmp ];
8889 # Upstream source
90+ version = "12.0.3";
9192+ lang = "ALL";
9394 srcs = {
95 x86_64-linux = fetchurl {
···99 "https://tor.eff.org/dist/torbrowser/${version}/tor-browser-linux64-${version}_${lang}.tar.xz"
100 "https://tor.calyxinstitute.org/dist/torbrowser/${version}/tor-browser-linux64-${version}_${lang}.tar.xz"
101 ];
102+ hash = "sha256-bOGY/RdwD6O7QIuOiBw7OVnZfpumGGso6hwMJJwN2g0=";
103 };
104105 i686-linux = fetchurl {
···109 "https://tor.eff.org/dist/torbrowser/${version}/tor-browser-linux32-${version}_${lang}.tar.xz"
110 "https://tor.calyxinstitute.org/dist/torbrowser/${version}/tor-browser-linux32-${version}_${lang}.tar.xz"
111 ];
112+ hash = "sha256-t1tnEZtiRig2r8GNJpqT+J0XoxCLMyUsI9tX6aa0lYk=";
113 };
114 };
115in
···293 # TBB will fail if ownership is too permissive
294 chmod 0700 "\$HOME/TorBrowser/Data/Tor"
295296+ # Initialize the browser profile state.
297+ # All files under user's profile dir are generated by TBB.
00298 mkdir -p "\$HOME/TorBrowser/Data/Browser/profile.default"
00299300 # Clear some files if the last known store path is different from the new one
301 : "\''${KNOWN_STORE_PATH:=\$HOME/known-store-path}"
···322 # Font cache files capture store paths; clear them out on the off
323 # chance that TBB would continue using old font files.
324 rm -rf "\$HOME/.cache/fontconfig"
325+326+ # Workaround a bug in 12.0.X that Tor directories are not cleaned up and tor gets confused where its socket is
327+ rm -rf \$XDG_RUNTIME_DIR/Tor*
328329 # Manually specify data paths (by default TB attempts to create these in the store)
330 {
···148 mutmut = throw "mutmut has been promoted to a top-level attribute"; # added 2022-10-02
149 net2grid = gridnet; # add 2022-04-22
150 nose-cover3 = throw "nose-cover3 has been removed, it was using setuptools 2to3 translation feature, which has been removed in setuptools 58"; # added 2022-02-16
0151 notifymuch = throw "notifymuch has been promoted to a top-level attribute"; # added 2022-10-02
152 Nuitka = nuitka; # added 2023-02-19
153 ordereddict = throw "ordereddict has been removed because it is only useful on unsupported python versions."; # added 2022-05-28
···148 mutmut = throw "mutmut has been promoted to a top-level attribute"; # added 2022-10-02
149 net2grid = gridnet; # add 2022-04-22
150 nose-cover3 = throw "nose-cover3 has been removed, it was using setuptools 2to3 translation feature, which has been removed in setuptools 58"; # added 2022-02-16
151+ nose_progressive = throw "nose_progressive has been removed, it was using setuptools 2to3 translation feature, which has been removed in setuptools 58"; #added 2023-02-21
152 notifymuch = throw "notifymuch has been promoted to a top-level attribute"; # added 2022-10-02
153 Nuitka = nuitka; # added 2023-02-19
154 ordereddict = throw "ordereddict has been removed because it is only useful on unsupported python versions."; # added 2022-05-28