diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix index 86033d02bf3f2..27a3a680b2575 100644 --- a/nixos/modules/services/misc/home-assistant.nix +++ b/nixos/modules/services/misc/home-assistant.nix @@ -5,10 +5,12 @@ with lib; let cfg = config.services.home-assistant; - # cfg.config != null can be assumed here + # Combined config + hassConfig = recursiveUpdate + (optionalAttrs cfg.applyDefaultConfig defaultConfig) + (optionalAttrs (cfg.config != null) cfg.config); configJSON = pkgs.writeText "configuration.json" - (builtins.toJSON (if cfg.applyDefaultConfig then - (recursiveUpdate defaultConfig cfg.config) else cfg.config)); + (builtins.toJSON hassConfig); configFile = pkgs.runCommand "configuration.yaml" { preferLocalBuild = true; } '' ${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out # Hack to support secrets, that are encoded as custom yaml objects, @@ -40,11 +42,11 @@ let # platform = "mqtt"; # ... # } ]; - useComponentPlatform = component: elem component (usedPlatforms cfg.config); + useComponentPlatform = component: elem component (usedPlatforms hassConfig); # Returns whether component is used in config useComponent = component: - hasAttrByPath (splitString "." component) cfg.config + hasAttrByPath (splitString "." component) hassConfig || useComponentPlatform component; # List of components used in config @@ -56,8 +58,10 @@ let # If you are changing this, please update the description in applyDefaultConfig defaultConfig = { - homeassistant.time_zone = config.time.timeZone; + frontend = {}; http.server_port = cfg.port; + } // optionalAttrs (config.time.timeZone != null) { + homeassistant.time_zone = config.time.timeZone; } // optionalAttrs (cfg.lovelaceConfig != null) { lovelace.mode = "yaml"; }; @@ -87,10 +91,9 @@ in { Setting this option enables a few configuration options for HA based on NixOS configuration (such as time zone) to avoid having to manually specify configuration we already have. - Currently one side effect of enabling this is that the http component will be enabled. + Currently one side effect of enabling this is that the http and frontend components will be enabled. - This only takes effect if config != null in order to ensure that a manually managed configuration.yaml is not overwritten. ''; }; @@ -225,7 +228,7 @@ in { systemd.services.home-assistant = { description = "Home Assistant"; after = [ "network.target" ]; - preStart = optionalString (cfg.config != null) (if cfg.configWritable then '' + preStart = (if cfg.configWritable then '' cp --no-preserve=mode ${configFile} "${cfg.configDir}/configuration.yaml" '' else '' rm -f "${cfg.configDir}/configuration.yaml" diff --git a/nixos/tests/home-assistant-config.nix b/nixos/tests/home-assistant-config.nix new file mode 100644 index 0000000000000..752066c3f9ffa --- /dev/null +++ b/nixos/tests/home-assistant-config.nix @@ -0,0 +1,65 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +let + configDir = "/var/lib/hass"; + configFile = "${configDir}/configuration.yaml"; + port = 4000; + + mkHass = { machineAttrs ? {}, hassAttrs ? {} }: + { pkgs, ... }: + { + services.home-assistant = { + inherit configDir port; + enable = true; + } // hassAttrs; + } // machineAttrs; +in { + name = "home-assistant-config"; + + nodes = { + hassMinimal = mkHass {}; + + hassTimeZone = mkHass { + machineAttrs = { time.timeZone = "UTC"; }; + }; + + hassNoDefault = mkHass { + hassAttrs = { applyDefaultConfig = false; }; + }; + }; + + testScript = let + portStr = toString port; + in '' + start_all() + + + def check_availability(hass): + hass.wait_for_unit("home-assistant.service") + with subtest("Check that Home Assistant can be reached via port ${portStr}"): + hass.wait_for_open_port(${portStr}) + hass.succeed("curl --fail http://localhost:${portStr}") + + + check_availability(hassMinimal) + with subtest("Check that port is set"): + config = hassMinimal.succeed("cat ${configFile}") + assert "server_port: ${portStr}" in config + + check_availability(hassTimeZone) + with subtest("Check that default_config, port and time_zone are set"): + config = hassTimeZone.succeed("cat ${configFile}") + assert "server_port: ${portStr}" in config + assert "time_zone: UTC" in config + + hassNoDefault.wait_for_unit("home-assistant.service") + + with subtest("Check that server port is not set"): + config = hassNoDefault.succeed("cat ${configFile}") + assert "server_port: ${portStr}" not in config + + # It cannot be reached because defaults (i.e. http.server_port, frontend) are not set + with subtest("Check that Home Assistant cannot be reached via port ${portStr}"): + hassNoDefault.wait_for_closed_port(${portStr}) + ''; +})