diff options
author | Martin Weinelt <mweinelt@users.noreply.github.com> | 2022-02-16 00:40:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-16 00:40:22 +0100 |
commit | e43b87552d411864d840204d2ad7685364ba7852 (patch) | |
tree | cbf279ed862f4f8d05ca17e0b9d303eedc317246 | |
parent | 9dee6cdb29175f464d640984eaf15017926072b0 (diff) | |
parent | 32bd0aafab0076ef09a3f256f61240c05daa0f4d (diff) |
Merge pull request #157213 from mweinelt/hass-module
-rw-r--r-- | nixos/doc/manual/from_md/release-notes/rl-2205.section.xml | 17 | ||||
-rw-r--r-- | nixos/doc/manual/release-notes/rl-2205.section.md | 9 | ||||
-rw-r--r-- | nixos/modules/module-list.nix | 2 | ||||
-rw-r--r-- | nixos/modules/services/home-automation/home-assistant.nix (renamed from nixos/modules/services/misc/home-assistant.nix) | 350 | ||||
-rw-r--r-- | nixos/tests/home-assistant.nix | 102 | ||||
-rw-r--r-- | pkgs/servers/home-assistant/default.nix | 13 | ||||
-rw-r--r-- | pkgs/servers/home-assistant/patches/tests-ignore-OSErrors-in-hass-fixture.patch | 27 |
7 files changed, 373 insertions, 147 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml index 4e64a02de81a4..4a6b539bcd0dd 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml @@ -271,6 +271,23 @@ </listitem> <listitem> <para> + The <literal>home-assistant</literal> module now requires + users that don’t want their configuration to be managed + declaratively to set + <literal>services.home-assistant.config = null;</literal>. + This is required due to the way default settings are handled + with the new settings style. + </para> + <para> + Additionally the default list of + <literal>extraComponents</literal> now includes the minimal + dependencies to successfully complete the + <link xlink:href="https://www.home-assistant.io/getting-started/onboarding/">onboarding</link> + procedure. + </para> + </listitem> + <listitem> + <para> <literal>pkgs.emacsPackages.orgPackages</literal> is removed because org elpa is deprecated. The packages in the top level of <literal>pkgs.emacsPackages</literal>, such as org and diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md index 10349f96d4a90..e06e7e385d102 100644 --- a/nixos/doc/manual/release-notes/rl-2205.section.md +++ b/nixos/doc/manual/release-notes/rl-2205.section.md @@ -91,6 +91,15 @@ In addition to numerous new and upgraded packages, this release has the followin `useLLVM`. So instead of `(ghc.withPackages (p: [])).override { withLLVM = true; }`, one needs to use `(ghc.withPackages.override { useLLVM = true; }) (p: [])`. +- The `home-assistant` module now requires users that don't want their + configuration to be managed declaratively to set + `services.home-assistant.config = null;`. This is required + due to the way default settings are handled with the new settings style. + + Additionally the default list of `extraComponents` now includes the minimal + dependencies to successfully complete the [onboarding](https://www.home-assistant.io/getting-started/onboarding/) + procedure. + - `pkgs.emacsPackages.orgPackages` is removed because org elpa is deprecated. The packages in the top level of `pkgs.emacsPackages`, such as org and org-contrib, refer to the ones in `pkgs.emacsPackages.elpaPackages` and diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index fa44d01b5aa46..5bfa35c3e9047 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -450,6 +450,7 @@ ./services/hardware/undervolt.nix ./services/hardware/vdr.nix ./services/hardware/xow.nix + ./services/home-automation/home-assistant.nix ./services/logging/SystemdJournal2Gelf.nix ./services/logging/awstats.nix ./services/logging/filebeat.nix @@ -545,7 +546,6 @@ ./services/misc/headphones.nix ./services/misc/heisenbridge.nix ./services/misc/greenclip.nix - ./services/misc/home-assistant.nix ./services/misc/ihaskell.nix ./services/misc/input-remapper.nix ./services/misc/irkerd.nix diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix index fc8ce08b2e136..bdd5e82bd2635 100644 --- a/nixos/modules/services/misc/home-assistant.nix +++ b/nixos/modules/services/home-automation/home-assistant.nix @@ -4,35 +4,27 @@ with lib; let cfg = config.services.home-assistant; + format = pkgs.formats.yaml {}; - # cfg.config != null can be assumed here - configJSON = pkgs.writeText "configuration.json" - (builtins.toJSON (if cfg.applyDefaultConfig then - (recursiveUpdate defaultConfig cfg.config) else cfg.config)); + # Render config attribute sets to YAML + # Values that are null will be filtered from the output, so this is one way to have optional + # options shown in settings. + # We post-process the result to add support for YAML functions, like secrets or includes, see e.g. + # https://www.home-assistant.io/docs/configuration/secrets/ + filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ null ])) cfg.config or {}; configFile = pkgs.runCommand "configuration.yaml" { preferLocalBuild = true; } '' - ${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out - # Hack to support custom yaml objects, - # i.e. secrets: https://www.home-assistant.io/docs/configuration/secrets/ + cp ${format.generate "configuration.yaml" filteredConfig} $out sed -i -e "s/'\!\([a-z_]\+\) \(.*\)'/\!\1 \2/;s/^\!\!/\!/;" $out ''; + lovelaceConfig = cfg.lovelaceConfig or {}; + lovelaceConfigFile = format.generate "ui-lovelace.yaml" lovelaceConfig; - lovelaceConfigJSON = pkgs.writeText "ui-lovelace.json" - (builtins.toJSON cfg.lovelaceConfig); - lovelaceConfigFile = pkgs.runCommand "ui-lovelace.yaml" { preferLocalBuild = true; } '' - ${pkgs.remarshal}/bin/json2yaml -i ${lovelaceConfigJSON} -o $out - ''; - + # Components advertised by the home-assistant package availableComponents = cfg.package.availableComponents; + # Components that were added by overriding the package explicitComponents = cfg.package.extraComponents; - - usedPlatforms = config: - if isAttrs config then - optional (config ? platform) config.platform - ++ concatMap usedPlatforms (attrValues config) - else if isList config then - concatMap usedPlatforms config - else [ ]; + useExplicitComponent = component: elem component explicitComponents; # Given a component "platform", looks up whether it is used in the config # as `platform = "platform";`. @@ -42,34 +34,46 @@ let # platform = "mqtt"; # ... # } ]; - useComponentPlatform = component: elem component (usedPlatforms cfg.config); + usedPlatforms = config: + if isAttrs config then + optional (config ? platform) config.platform + ++ concatMap usedPlatforms (attrValues config) + else if isList config then + concatMap usedPlatforms config + else [ ]; - useExplicitComponent = component: elem component explicitComponents; + useComponentPlatform = component: elem component (usedPlatforms cfg.config); - # Returns whether component is used in config or explicitly passed into package + # Returns whether component is used in config, explicitly passed into package or + # configured in the module. useComponent = component: hasAttrByPath (splitString "." component) cfg.config || useComponentPlatform component - || useExplicitComponent component; + || useExplicitComponent component + || builtins.elem component cfg.extraComponents; - # List of components used in config + # Final list of components passed into the package to include required dependencies extraComponents = filter useComponent availableComponents; - package = if (cfg.autoExtraComponents && cfg.config != null) - then (cfg.package.override { inherit extraComponents; }) - else cfg.package; + package = (cfg.package.override (oldArgs: { + # Respect overrides that already exist in the passed package and + # concat it with values passed via the module. + extraComponents = oldArgs.extraComponents ++ extraComponents; + extraPackages = ps: (oldArgs.extraPackages ps) ++ (cfg.extraPackages ps); + })); +in { + imports = [ + # Migrations in NixOS 22.05 + (mkRemovedOptionModule [ "services" "home-assistant" "applyDefaultConfig" ] "The default config was migrated into services.home-assistant.config") + (mkRemovedOptionModule [ "services" "home-assistant" "autoExtraComponents" ] "Components are now parsed from services.home-assistant.config unconditionally") + (mkRenamedOptionModule [ "services" "home-assistant" "port" ] [ "services" "home-assistant" "config" "http" "server_port" ]) + ]; - # If you are changing this, please update the description in applyDefaultConfig - defaultConfig = { - homeassistant.time_zone = config.time.timeZone; - http.server_port = cfg.port; - } // optionalAttrs (cfg.lovelaceConfig != null) { - lovelace.mode = "yaml"; + meta = { + buildDocsInSandbox = false; + maintainers = teams.home-assistant.members; }; -in { - meta.maintainers = teams.home-assistant.members; - options.services.home-assistant = { # Running home-assistant on NixOS is considered an installation method that is unsupported by the upstream project. # https://github.com/home-assistant/architecture/blob/master/adr/0012-define-supported-installation-method.md#decision @@ -81,42 +85,166 @@ in { description = "The config directory, where your <filename>configuration.yaml</filename> is located."; }; - port = mkOption { - default = 8123; - type = types.port; - description = "The port on which to listen."; + extraComponents = mkOption { + type = types.listOf (types.enum availableComponents); + default = [ + # List of components required to complete the onboarding + "default_config" + "met" + "esphome" + ] ++ optionals (pkgs.stdenv.hostPlatform.isAarch32 || pkgs.stdenv.hostPlatform.isAarch64) [ + # Use the platform as an indicator that we might be running on a RaspberryPi and include + # relevant components + "rpi_power" + ]; + example = literalExpression '' + [ + "analytics" + "default_config" + "esphome" + "my" + "shopping_list" + "wled" + ] + ''; + description = '' + List of <link xlink:href="https://www.home-assistant.io/integrations/">components</link> that have their dependencies included in the package. + + The component name can be found in the URL, for example <literal>https://www.home-assistant.io/integrations/ffmpeg/</literal> would map to <literal>ffmpeg</literal>. + ''; }; - applyDefaultConfig = mkOption { - default = true; - type = types.bool; + extraPackages = mkOption { + type = types.functionTo (types.listOf types.package); + default = _: []; + defaultText = literalExpression '' + python3Packages: with python3Packages; []; + ''; + example = literalExpression '' + python3Packages: with python3Packages; [ + # postgresql support + psycopg2 + ]; + ''; description = '' - 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. - </para> - <para> - Currently one side effect of enabling this is that the <literal>http</literal> component will be enabled. - </para> - <para> - This only takes effect if <literal>config != null</literal> in order to ensure that a manually managed <filename>configuration.yaml</filename> is not overwritten. + List of packages to add to propagatedBuildInputs. + + A popular example is <package>python3Packages.psycopg2</package> + for PostgreSQL support in the recorder component. ''; }; config = mkOption { - default = null; - # Migrate to new option types later: https://github.com/NixOS/nixpkgs/pull/75584 - type = with lib.types; let - valueType = nullOr (oneOf [ - bool - int - float - str - (lazyAttrsOf valueType) - (listOf valueType) - ]) // { - description = "Yaml value"; - emptyValue.value = {}; + type = types.submodule { + freeformType = format.type; + options = { + # This is a partial selection of the most common options, so new users can quickly + # pick up how to match home-assistants config structure to ours. It also lets us preset + # config values intelligently. + + homeassistant = { + # https://www.home-assistant.io/docs/configuration/basic/ + name = mkOption { + type = types.nullOr types.str; + default = null; + example = "Home"; + description = '' + Name of the location where Home Assistant is running. + ''; + }; + + latitude = mkOption { + type = types.nullOr (types.either types.float types.str); + default = null; + example = 52.3; + description = '' + Latitude of your location required to calculate the time the sun rises and sets. + ''; + }; + + longitude = mkOption { + type = types.nullOr (types.either types.float types.str); + default = null; + example = 4.9; + description = '' + Longitude of your location required to calculate the time the sun rises and sets. + ''; + }; + + unit_system = mkOption { + type = types.nullOr (types.enum [ "metric" "imperial" ]); + default = null; + example = "metric"; + description = '' + The unit system to use. This also sets temperature_unit, Celsius for Metric and Fahrenheit for Imperial. + ''; + }; + + temperature_unit = mkOption { + type = types.nullOr (types.enum [ "C" "F" ]); + default = null; + example = "C"; + description = '' + Override temperature unit set by unit_system. <literal>C</literal> for Celsius, <literal>F</literal> for Fahrenheit. + ''; + }; + + time_zone = mkOption { + type = types.nullOr types.str; + default = config.time.timeZone or null; + defaultText = literalExpression '' + config.time.timeZone or null + ''; + example = "Europe/Amsterdam"; + description = '' + Pick your time zone from the column TZ of Wikipedia’s <link xlink:href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">list of tz database time zones</link>. + ''; + }; + }; + + http = { + # https://www.home-assistant.io/integrations/http/ + server_host = mkOption { + type = types.either types.str (types.listOf types.str); + default = [ + "0.0.0.0" + "::" + ]; + example = "::1"; + description = '' + Only listen to incoming requests on specific IP/host. The default listed assumes support for IPv4 and IPv6. + ''; + }; + + server_port = mkOption { + default = 8123; + type = types.port; + description = '' + The port on which to listen. + ''; + }; }; - in valueType; + + lovelace = { + # https://www.home-assistant.io/lovelace/dashboards/ + mode = mkOption { + type = types.enum [ "yaml" "storage" ]; + default = if cfg.lovelaceConfig != null + then "yaml" + else "storage"; + defaultText = literalExpression '' + if cfg.lovelaceConfig != null + then "yaml" + else "storage"; + ''; + example = "yaml"; + description = '' + In what mode should the main Lovelace panel be, <literal>yaml</literal> or <literal>storage</literal> (UI managed). + ''; + }; + }; + }; + }; example = literalExpression '' { homeassistant = { @@ -130,15 +258,19 @@ in { frontend = { themes = "!include_dir_merge_named themes"; }; - http = { }; + http = {}; feedreader.urls = [ "https://nixos.org/blogs.xml" ]; } ''; description = '' Your <filename>configuration.yaml</filename> as a Nix attribute set. - Beware that setting this option will delete your previous <filename>configuration.yaml</filename>. - <link xlink:href="https://www.home-assistant.io/docs/configuration/secrets/">Secrets</link> - are encoded as strings as shown in the example. + + YAML functions like <link xlink:href="https://www.home-assistant.io/docs/configuration/secrets/">secrets</link> + can be passed as a string and will be unquoted automatically. + + Unless this option is explicitly set to <literal>null</literal> + we assume your <filename>configuration.yaml</filename> is + managed through this module and thereby overwritten on startup. ''; }; @@ -147,16 +279,18 @@ in { type = types.bool; description = '' Whether to make <filename>configuration.yaml</filename> writable. - This only has an effect if <option>config</option> is set. + This will allow you to edit it from Home Assistant's web interface. + + This only has an effect if <option>config</option> is set. However, bear in mind that it will be overwritten at every start of the service. ''; }; lovelaceConfig = mkOption { default = null; - type = with types; nullOr attrs; - # from https://www.home-assistant.io/lovelace/yaml-mode/ + type = types.nullOr format.type; + # from https://www.home-assistant.io/lovelace/dashboards/ example = literalExpression '' { title = "My Awesome Home"; @@ -172,8 +306,8 @@ in { ''; description = '' Your <filename>ui-lovelace.yaml</filename> as a Nix attribute set. - Setting this option will automatically add - <literal>lovelace.mode = "yaml";</literal> to your <option>config</option>. + Setting this option will automatically set <literal>lovelace.mode</literal> to <literal>yaml</literal>. + Beware that setting this option will delete your previous <filename>ui-lovelace.yaml</filename> ''; }; @@ -183,8 +317,10 @@ in { type = types.bool; description = '' Whether to make <filename>ui-lovelace.yaml</filename> writable. - This only has an effect if <option>lovelaceConfig</option> is set. + This will allow you to edit it from Home Assistant's web interface. + + This only has an effect if <option>lovelaceConfig</option> is set. However, bear in mind that it will be overwritten at every start of the service. ''; }; @@ -201,11 +337,18 @@ in { type = types.package; example = literalExpression '' pkgs.home-assistant.override { - extraPackages = ps: with ps; [ colorlog ]; + extraPackages = python3Packages: with python3Packages; [ + psycopg2 + ]; + extraComponents = [ + "default_config" + "esphome" + "met" + ]; } ''; description = '' - Home Assistant package to use. By default the tests are disabled, as they take a considerable amout of time to complete. + The Home Assistant package to use. Override <literal>extraPackages</literal> or <literal>extraComponents</literal> in order to add additional dependencies. If you specify <option>config</option> and do not set <option>autoExtraComponents</option> to <literal>false</literal>, overriding <literal>extraComponents</literal> will have no effect. @@ -213,21 +356,6 @@ in { ''; }; - autoExtraComponents = mkOption { - default = true; - type = types.bool; - description = '' - If set to <literal>true</literal>, the components used in <literal>config</literal> - are set as the specified package's <literal>extraComponents</literal>. - This in turn adds all packaged dependencies to the derivation. - You might still see import errors in your log. - In this case, you will need to package the necessary dependencies yourself - or ask for someone else to package them. - If a dependency is packaged but not automatically added to this list, - you might need to specify it in <literal>extraPackages</literal>. - ''; - }; - openFirewall = mkOption { default = false; type = types.bool; @@ -240,18 +368,30 @@ in { systemd.services.home-assistant = { description = "Home Assistant"; - after = [ "network.target" ]; - preStart = optionalString (cfg.config != null) (if cfg.configWritable then '' - cp --no-preserve=mode ${configFile} "${cfg.configDir}/configuration.yaml" - '' else '' - rm -f "${cfg.configDir}/configuration.yaml" - ln -s ${configFile} "${cfg.configDir}/configuration.yaml" - '') + optionalString (cfg.lovelaceConfig != null) (if cfg.lovelaceConfigWritable then '' - cp --no-preserve=mode ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml" - '' else '' - rm -f "${cfg.configDir}/ui-lovelace.yaml" - ln -s ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml" - ''); + after = [ + "network-online.target" + + # prevent races with database creation + "mysql.service" + "postgresql.service" + ]; + preStart = let + copyConfig = if cfg.configWritable then '' + cp --no-preserve=mode ${configFile} "${cfg.configDir}/configuration.yaml" + '' else '' + rm -f "${cfg.configDir}/configuration.yaml" + ln -s ${configFile} "${cfg.configDir}/configuration.yaml" + ''; + copyLovelaceConfig = if cfg.lovelaceConfigWritable then '' + cp --no-preserve=mode ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml" + '' else '' + rm -f "${cfg.configDir}/ui-lovelace.yaml" + ln -s ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml" + ''; + in + (optionalString (cfg.config != null) copyConfig) + + (optionalString (cfg.lovelaceConfig != null) copyLovelaceConfig) + ; serviceConfig = let # List of capabilities to equip home-assistant with, depending on configured components capabilities = [ diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix index 5b1c07c92da35..31f8a4bcc1981 100644 --- a/nixos/tests/home-assistant.nix +++ b/nixos/tests/home-assistant.nix @@ -10,6 +10,7 @@ in { nodes.hass = { pkgs, ... }: { environment.systemPackages = with pkgs; [ mosquitto ]; + services.mosquitto = { enable = true; listeners = [ { @@ -21,14 +22,42 @@ in { }; } ]; }; + + services.postgresql = { + enable = true; + ensureDatabases = [ "hass" ]; + ensureUsers = [{ + name = "hass"; + ensurePermissions = { + "DATABASE hass" = "ALL PRIVILEGES"; + }; + }]; + }; + services.home-assistant = { - inherit configDir; enable = true; + inherit configDir; + + # tests loading components by overriding the package package = (pkgs.home-assistant.override { + extraPackages = ps: with ps; [ + colorama + ]; extraComponents = [ "zha" ]; }).overrideAttrs (oldAttrs: { doInstallCheck = false; }); + + # tests loading components from the module + extraComponents = [ + "wake_on_lan" + ]; + + # test extra package passing from the module + extraPackages = python3Packages: with python3Packages; [ + psycopg2 + ]; + config = { homeassistant = { name = "Home"; @@ -37,34 +66,58 @@ in { longitude = "0.0"; elevation = 0; }; + + # configure the recorder component to use the postgresql db + recorder.db_url = "postgresql://@/hass"; + + # we can't load default_config, because the updater requires + # network access and would cause an error, so load frontend + # here explicitly. + # https://www.home-assistant.io/integrations/frontend/ frontend = {}; + + # configure an mqtt broker connection + # https://www.home-assistant.io/integrations/mqtt mqtt = { broker = "127.0.0.1"; username = mqttUsername; password = mqttPassword; }; - binary_sensor = [{ + + # create a mqtt sensor that syncs state with its mqtt topic + # https://www.home-assistant.io/integrations/sensor.mqtt/ + binary_sensor = [ { platform = "mqtt"; state_topic = "home-assistant/test"; payload_on = "let_there_be_light"; payload_off = "off"; - }]; - wake_on_lan = {}; - switch = [{ + } ]; + + # set up a wake-on-lan switch to test capset capability required + # for the ping suid wrapper + # https://www.home-assistant.io/integrations/wake_on_lan/ + switch = [ { platform = "wake_on_lan"; mac = "00:11:22:33:44:55"; host = "127.0.0.1"; - }]; - # tests component-based capability assignment (CAP_NET_BIND_SERVICE) + } ]; + + # test component-based capability assignment (CAP_NET_BIND_SERVICE) + # https://www.home-assistant.io/integrations/emulated_hue/ emulated_hue = { host_ip = "127.0.0.1"; listen_port = 80; }; + + # show mqtt interaction in the log + # https://www.home-assistant.io/integrations/logger/ logger = { default = "info"; logs."homeassistant.components.mqtt" = "debug"; }; }; + + # configure the sample lovelace dashboard lovelaceConfig = { title = "My Awesome Home"; views = [{ @@ -81,34 +134,57 @@ in { }; testScript = '' + import re + start_all() + + # Parse the package path out of the systemd unit, as we cannot + # access the final package, that is overriden inside the module, + # by any other means. + pattern = re.compile(r"path=(?P<path>[\/a-z0-9-.]+)\/bin\/hass") + response = hass.execute("systemctl show -p ExecStart home-assistant.service")[1] + match = pattern.search(response) + package = match.group('path') + hass.wait_for_unit("home-assistant.service") + with subtest("Check that YAML configuration file is in place"): hass.succeed("test -L ${configDir}/configuration.yaml") - with subtest("lovelace config is copied because lovelaceConfigWritable = true"): + + with subtest("Check the lovelace config is copied because lovelaceConfigWritable = true"): hass.succeed("test -f ${configDir}/ui-lovelace.yaml") + + with subtest("Check extraComponents and extraPackages are considered from the package"): + hass.succeed(f"grep -q 'colorama' {package}/extra_packages") + hass.succeed(f"grep -q 'zha' {package}/extra_components") + + with subtest("Check extraComponents and extraPackages are considered from the module"): + hass.succeed(f"grep -q 'psycopg2' {package}/extra_packages") + hass.succeed(f"grep -q 'wake_on_lan' {package}/extra_components") + with subtest("Check that Home Assistant's web interface and API can be reached"): + hass.wait_until_succeeds("journalctl -u home-assistant.service | grep -q 'Home Assistant initialized in'") hass.wait_for_open_port(8123) hass.succeed("curl --fail http://localhost:8123/lovelace") + with subtest("Toggle a binary sensor using MQTT"): hass.wait_for_open_port(1883) hass.succeed( "mosquitto_pub -V mqttv5 -t home-assistant/test -u ${mqttUsername} -P '${mqttPassword}' -m let_there_be_light" ) + with subtest("Check that capabilities are passed for emulated_hue to bind to port 80"): hass.wait_for_open_port(80) hass.succeed("curl --fail http://localhost:80/description.xml") + with subtest("Check extra components are considered in systemd unit hardening"): hass.succeed("systemctl show -p DeviceAllow home-assistant.service | grep -q char-ttyUSB") + with subtest("Print log to ease debugging"): output_log = hass.succeed("cat ${configDir}/home-assistant.log") print("\n### home-assistant.log ###\n") print(output_log + "\n") - # wait for home-assistant to fully boot - hass.sleep(30) - hass.wait_for_unit("home-assistant.service") - with subtest("Check that no errors were logged"): assert "ERROR" not in output_log @@ -117,7 +193,7 @@ in { assert "let_there_be_light" in output_log with subtest("Check systemd unit hardening"): - hass.log(hass.succeed("systemctl show home-assistant.service")) + hass.log(hass.succeed("systemctl cat home-assistant.service")) hass.log(hass.succeed("systemd-analyze security home-assistant.service")) ''; }) diff --git a/pkgs/servers/home-assistant/default.nix b/pkgs/servers/home-assistant/default.nix index 3d446f1f2749b..b000431fd1c7c 100644 --- a/pkgs/servers/home-assistant/default.nix +++ b/pkgs/servers/home-assistant/default.nix @@ -15,6 +15,9 @@ # Additional packages to add to propagatedBuildInputs , extraPackages ? ps: [] +# Write out info about included extraComponents and extraPackages +, writeText + # Override Python packages using # self: super: { pkg = super.pkg.overridePythonAttrs (oldAttrs: { ... }); } # Applied after defaultOverrides @@ -130,6 +133,10 @@ let # Ensure that we are using a consistent package set extraBuildInputs = extraPackages python.pkgs; + # Create info about included packages and components + extraComponentsFile = writeText "home-assistant-components" (lib.concatStringsSep "\n" extraComponents); + extraPackagesFile = writeText "home-assistant-packages" (lib.concatMapStringsSep "\n" (pkg: pkg.pname) extraBuildInputs); + # Don't forget to run parse-requirements.py after updating hassVersion = "2022.2.6"; @@ -158,7 +165,6 @@ in python.pkgs.buildPythonApplication rec { src = ./patches/ffmpeg-path.patch; ffmpeg = "${lib.getBin ffmpeg}/bin/ffmpeg"; }) - ./patches/tests-ignore-OSErrors-in-hass-fixture.patch ]; postPatch = let @@ -284,6 +290,11 @@ in python.pkgs.buildPythonApplication rec { export PATH=${inetutils}/bin:$PATH ''; + postInstall = '' + cp -v ${extraComponentsFile} $out/extra_components + cp -v ${extraPackagesFile} $out/extra_packages + ''; + passthru = { inherit availableComponents diff --git a/pkgs/servers/home-assistant/patches/tests-ignore-OSErrors-in-hass-fixture.patch b/pkgs/servers/home-assistant/patches/tests-ignore-OSErrors-in-hass-fixture.patch deleted file mode 100644 index add0ea1d5521f..0000000000000 --- a/pkgs/servers/home-assistant/patches/tests-ignore-OSErrors-in-hass-fixture.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 3e3f5c37252a33ea1e71c39f2ca0f13940c261ad Mon Sep 17 00:00:00 2001 -From: Martin Weinelt <hexa@darmstadt.ccc.de> -Date: Sat, 17 Jul 2021 16:11:23 +0200 -Subject: [PATCH] tests: ignore OSErrors in hass fixture - -The nix sandbox will cause OSErrors due to limitations imposed on -network interaction. This change makes it so we forgive these cases. ---- - tests/conftest.py | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/tests/conftest.py b/tests/conftest.py -index 1f5ffc80d0..b284727a0f 100644 ---- a/tests/conftest.py -+++ b/tests/conftest.py -@@ -168,6 +168,8 @@ def hass(loop, load_registries, hass_storage, request): - continue - if isinstance(ex, ServiceNotFound): - continue -+ if isinstance(ex, OSError): -+ continue - raise ex - - --- -2.32.0 - |