diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services/misc/home-assistant.nix')
-rw-r--r-- | nixpkgs/nixos/modules/services/misc/home-assistant.nix | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/misc/home-assistant.nix b/nixpkgs/nixos/modules/services/misc/home-assistant.nix new file mode 100644 index 00000000000..74702c97f55 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/home-assistant.nix @@ -0,0 +1,250 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.home-assistant; + + # 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)); + configFile = pkgs.runCommand "configuration.yaml" { preferLocalBuild = true; } '' + ${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out + ''; + + 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 + ''; + + availableComponents = cfg.package.availableComponents; + + usedPlatforms = config: + if isAttrs config then + optional (config ? platform) config.platform + ++ concatMap usedPlatforms (attrValues config) + else if isList config then + concatMap usedPlatforms config + else [ ]; + + # Given a component "platform", looks up whether it is used in the config + # as `platform = "platform";`. + # + # For example, the component mqtt.sensor is used as follows: + # config.sensor = [ { + # platform = "mqtt"; + # ... + # } ]; + useComponentPlatform = component: elem component (usedPlatforms cfg.config); + + # Returns whether component is used in config + useComponent = component: + hasAttrByPath (splitString "." component) cfg.config + || useComponentPlatform component; + + # List of components used in config + 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"; + }; + +in { + meta.maintainers = with maintainers; [ dotlambda ]; + + options.services.home-assistant = { + enable = mkEnableOption "Home Assistant"; + + configDir = mkOption { + default = "/var/lib/hass"; + type = types.path; + description = "The config directory, where your <filename>configuration.yaml</filename> is located."; + }; + + port = mkOption { + default = 8123; + type = types.int; + description = "The port on which to listen."; + }; + + applyDefaultConfig = mkOption { + default = true; + type = types.bool; + 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. + ''; + }; + + config = mkOption { + default = null; + type = with types; nullOr attrs; + example = literalExample '' + { + homeassistant = { + name = "Home"; + time_zone = "UTC"; + }; + frontend = { }; + 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>. + ''; + }; + + configWritable = mkOption { + default = false; + 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. + 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/ + example = literalExample '' + { + title = "My Awesome Home"; + views = [ { + title = "Example"; + cards = [ { + type = "markdown"; + title = "Lovelace"; + content = "Welcome to your **Lovelace UI**."; + } ]; + } ]; + } + ''; + 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>. + Beware that setting this option will delete your previous <filename>ui-lovelace.yaml</filename> + ''; + }; + + lovelaceConfigWritable = mkOption { + default = false; + 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. + However, bear in mind that it will be overwritten at every start of the service. + ''; + }; + + package = mkOption { + default = pkgs.home-assistant; + defaultText = "pkgs.home-assistant"; + type = types.package; + example = literalExample '' + pkgs.home-assistant.override { + extraPackages = ps: with ps; [ colorlog ]; + } + ''; + description = '' + 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. + ''; + }; + + 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; + description = "Whether to open the firewall for the specified port."; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; + + 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" + ''); + serviceConfig = { + ExecStart = "${package}/bin/hass --config '${cfg.configDir}'"; + User = "hass"; + Group = "hass"; + Restart = "on-failure"; + ProtectSystem = "strict"; + ReadWritePaths = "${cfg.configDir}"; + KillSignal = "SIGINT"; + PrivateTmp = true; + RemoveIPC = true; + AmbientCapabilities = "cap_net_raw,cap_net_admin+eip"; + }; + path = [ + "/run/wrappers" # needed for ping + ]; + }; + + systemd.targets.home-assistant = rec { + description = "Home Assistant"; + wantedBy = [ "multi-user.target" ]; + wants = [ "home-assistant.service" ]; + after = wants; + }; + + users.users.hass = { + home = cfg.configDir; + createHome = true; + group = "hass"; + uid = config.ids.uids.hass; + }; + + users.groups.hass.gid = config.ids.gids.hass; + }; +} |