Merge pull request #157213 from mweinelt/hass-module
This commit is contained in:
commit
e43b87552d
7 changed files with 375 additions and 149 deletions
|
@ -269,6 +269,23 @@
|
|||
<literal>(ghc.withPackages.override { useLLVM = true; }) (p: [])</literal>.
|
||||
</para>
|
||||
</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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,33 +34,45 @@ let
|
|||
# platform = "mqtt";
|
||||
# ...
|
||||
# } ];
|
||||
usedPlatforms = config:
|
||||
if isAttrs config then
|
||||
optional (config ? platform) config.platform
|
||||
++ concatMap usedPlatforms (attrValues config)
|
||||
else if isList config then
|
||||
concatMap usedPlatforms config
|
||||
else [ ];
|
||||
|
||||
useComponentPlatform = component: elem component (usedPlatforms cfg.config);
|
||||
|
||||
useExplicitComponent = component: elem component explicitComponents;
|
||||
|
||||
# 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;
|
||||
|
||||
# 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";
|
||||
};
|
||||
|
||||
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 {
|
||||
meta.maintainers = teams.home-assistant.members;
|
||||
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" ])
|
||||
];
|
||||
|
||||
meta = {
|
||||
buildDocsInSandbox = false;
|
||||
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.
|
||||
|
@ -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>.
|
||||
'';
|
||||
};
|
||||
};
|
||||
in valueType;
|
||||
|
||||
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.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
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 = [
|
|
@ -10,6 +10,7 @@ in {
|
|||
|
||||
nodes.hass = { pkgs, ... }: {
|
||||
environment.systemPackages = with pkgs; [ mosquitto ];
|
||||
|
||||
services.mosquitto = {
|
||||
enable = true;
|
||||
listeners = [ {
|
||||
|
@ -21,14 +22,42 @@ in {
|
|||
};
|
||||
} ];
|
||||
};
|
||||
services.home-assistant = {
|
||||
inherit configDir;
|
||||
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [ "hass" ];
|
||||
ensureUsers = [{
|
||||
name = "hass";
|
||||
ensurePermissions = {
|
||||
"DATABASE hass" = "ALL PRIVILEGES";
|
||||
};
|
||||
}];
|
||||
};
|
||||
|
||||
services.home-assistant = {
|
||||
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"))
|
||||
'';
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
Loading…
Reference in a new issue