Home Assistant: Difference between revisions

From NixOS Wiki
imported>Mweinelt
Hexa (talk | contribs)
Refresh and deduplicate content
 
(42 intermediate revisions by 11 users not shown)
Line 1: Line 1:
[https://www.home-assistant.io/ Home Assistant] is an open source home automation software that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts.
[[File:New Home Assistant logo.svg|right|250px]]


= Installation methods =
[https://www.home-assistant.io/ Home Assistant] is an open source home automation software that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts.


Upstream has defined several installation methods which they are willing to support. The NixOS module is not one of them. When you find a problem you can still report it upstream, if you are certain that the issue is relevant to upstream supported installation methods as well. If not, or if in doubt, please open an isssue on the nixpkgs issue tracker or visit the [https://matrix.to/#/#nixos-homeautomation:lossy.network?via=lossy.network&via=matrix.org&via=kack.it NixOS Home-Automation] (#nixos-homeautomation:lossy.network) Matrix room.
NixOS provides native support for [https://www.home-assistant.io/faq/ha-vs-hassio/ Home Assistant Core] and offers integration facilities for most pieces of its comprehensive ecosystem:


== Upstream supported ==
* As of the 2024.12.4 release we support roughly 90.7% (1176/1296) of the built-in integrations
=== Virtual machine ===
* We support [https://github.com/NixOS/nixpkgs/tree/master/pkgs/servers/home-assistant/custom-components custom components] through the <code>[https://search.nixos.org/options?channel=unstable&show=services.home-assistant.customComponents&from=0&size=50&sort=relevance&type=packages&query=services.home-assistant.customComponents services.home-assistant.customComponents]</code> option
* We support source-built [https://github.com/NixOS/nixpkgs/tree/master/pkgs/servers/home-assistant/custom-lovelace-modules custom lovelace modules] mostly through [https://search.nixos.org/options?channel=unstable&show=services.home-assistant.customLovelaceModules&from=0&size=50&sort=relevance&type=packages&query=services.home-assistant.customLovelaceModules services.home-assistant.customLovelaceModules] option
* We do not support [https://www.home-assistant.io/addons/ addons], which are used to deploy additional services, that are configurable from Home Assistant on their operating system
** NixOS has native support for various services, that integrate with Home Assistant, e.g. [[Mosquitto]], [[Music-Assistant]], [[Wyoming]], [[zigbee2mqtt]], [[Z-Wave JS]]


You could run your Home Assistant in a virtual machine, with your NixOS host providing the hypervisor.
== Support ==
Depending on the installation method one of various support channels should be used.


* [https://www.home-assistant.io/installation/linux#install-home-assistant-operating-system Install Home Assistant Operating System (home-assistant.io)]
If you rely on the NixOS package and/or module, issues should be reported on the [https://github.com/NixOS/nixpkgs/issues/new/choose nixpkgs Issue tracker] or the [https://matrix.to/#/#homeautomation:nixos.org?via=lossy.network&via=matrix.org&via=kack.it #homeautomation:nixos.org] Matrix room.
* [https://myme.no/posts/2021-11-25-nixos-home-assistant.html NixOS: Headless Home Assistant VM (myme.no)]


'''TODO:''' add example configuration
Only if you rely on one of the [[Home Assistant#Upstream supported|upstream supported]] deployment methods issues can be directly reported to the upstream project. Make sure to follow their guide on [https://www.home-assistant.io/help/reporting_issues/ reporting issues].


=== OCI container ===
== Upstream installation methods ==
If you intend for Home Assistant to be an end-user configurable experience, as opposed to the declarative configuration experience NixOS offers, then consider these setups. They have the benefit of full upstream support.


You could run your Home Assistant in any kind of container runtime
=== Virtual machine ===
[[File:Home Assistant OS in Virtualbox.png|alt=Home Assistant OS in Virtualbox|thumb|HAOS in Virtualbox]]
Home Assistant maintains their own operating system and provides [[QEMU]] (qcow2) and [[Virtualbox]] (vdi) compatible [https://www.home-assistant.io/installation/linux#install-home-assistant-operating-system images]. NixOS supports virtualization solutions like [[libvirt]] and [[Incus]], both of which wrap QEMU, and [[Virtualbox]].


* [https://www.home-assistant.io/installation/linux#install-home-assistant-container Install Home Assistant Container (home-assistant.io)]
Example:
* [https://myme.no/posts/2021-11-25-nixos-home-assistant.html NixOS: Headless Home Assistant VM (myme.no)] using libvirt


==== Example Configuration ====
=== OCI container ===
Home Assistant also provides a [https://www.home-assistant.io/installation/linux#install-home-assistant-container container image] for OCI compatible runtimes.


Adding the following to your `configuration.nix` will bring up home-assistant listening on `:8123` on the host interface.  
The following example configuration uses [[podman]] to download and run the <code>home-assistant:stable</code> image. The frontend will be available via HTTP on port <code>tcp/8123</code> in the host network namespace and can be reverse proxied from there.  


<syntaxHighlight lang=nix>
<syntaxhighlight lang="nix">
{   
{   
   virtualisation.oci-containers = {
   virtualisation.oci-containers = {
Line 32: Line 40:
       volumes = [ "home-assistant:/config" ];
       volumes = [ "home-assistant:/config" ];
       environment.TZ = "Europe/Berlin";
       environment.TZ = "Europe/Berlin";
       image = "ghcr.io/home-assistant/home-assistant:stable"; # Warning: if the tag does not change, the image will not be updated
      # Note: The image will not be updated on rebuilds, unless the version label changes
       image = "ghcr.io/home-assistant/home-assistant:stable";
       extraOptions = [  
       extraOptions = [  
         "--network=host"  
        # Use the host network namespace for all sockets
         "--device=/dev/ttyACM0:/dev/ttyACM0" # Example, change this to match your own hardware
         "--network=host"
        # Pass devices into the container, so Home Assistant can discover and make use of them
         "--device=/dev/ttyACM0:/dev/ttyACM0"
       ];
       ];
     };
     };
   };
   };
}
}
</syntaxHighlight>
</syntaxhighlight>


== NixOS Module ==
== Native installation ==


You could run home-assistant using the NixOS module system at <code>services-home-assistant</code>. As of 2021-11-28 we have roughly 70% of component dependencies packaged. There are two major ways of running home-assistant using the module system.
* There is full support for the YAML configuration through the <code>[https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=services.home-assistant.config services.home-assistant.config]</code> option.
* This is also the case for the [https://www.home-assistant.io/dashboards/dashboards/#adding-more-dashboards-with-yaml Lovelace YAML configuration] through the [https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=services.home-assistant.lovelace <code>services.home-assistant.lovelace</code>] option.
** Custom Lovelace modules can be configured through [https://search.nixos.org/options?channel=unstable&show=services.home-assistant.customLovelaceModules&from=0&size=50&sort=relevance&type=packages&query=services.home-assistant.customLovelaceModules <code>services.home-assistant.customLovelaceModules</code>]. The [https://www.home-assistant.io/dashboards/dashboards/#resources <code>resources</code>] section of your Lovelace configuration will automatically be populated.
* Custom components can be enabled through the [https://search.nixos.org/options?channel=unstable&show=services.home-assistant.customComponents&from=0&size=50&sort=relevance&type=packages&query=services.home-assistant.customComponents <code>services.home-assistant.customComponents</code>].


=== Declarative configuration ===
=== Declarative configuration ===
Set up your home-assistant by configuring the <code>services.home-assistant.config</code> attribute set as if it were your home-assistant  [https://www.home-assistant.io/docs/configuration/yaml/ YAML configuration]. The module parses the root and platforms level to automatically discover integrations used and will provide their dependencies to your home-assistant package.


Set up your home-assistant by configuring the <code>services.home-assistant.config</code> attribute set as if it were your home-assistant YAML configuration. The module parses the root and platforms level to automatically discover integrations used and will add their dependencies to your home-assistant package.
The following is a minimal configuration, that has all the dependencies that are required to complete the initial configuration flow, that creates your first user:


The following is a minimal starting configuration, that has all the dependencies that are required to complete the initial configuration flow, that creates your first user.
<syntaxhighlight lang="nix">
 
<syntaxHighlight lang=nix>
{
{
   services.home-assistant = {
   services.home-assistant = {
Line 58: Line 71:
     extraComponents = [
     extraComponents = [
       # Components required to complete the onboarding
       # Components required to complete the onboarding
      "analytics"
      "google_translate"
       "met"
       "met"
       "radio_browser"
       "radio_browser"
      "shopping_list"
      # Recommended for fast zlib compression
      # https://www.home-assistant.io/integrations/isal
      "isal"
     ];
     ];
     config = {
     config = {
Line 68: Line 87:
   };
   };
}
}
</syntaxHighlight>
</syntaxhighlight>
 
=== Imperative configuration ===
Alternatively, If you would like to manage your configuration outside your NixOS configuration, you can set up the module to pass a configuration directory. This kind of setup is useful if you want to gradually migrate your existing configuration over.
 
Using a custom configuration has the drawback, that we cannot automatically recognize and install component dependencies, and it is unlikely that we will continue to support these kinds of setups going forward.<syntaxhighlight lang="nix">
{
  services.home-assistant = {
    # opt-out from declarative configuration management
    config = null;
    lovelaceConfig = null;
    # configure the path to your config directory
    configDir = "/etc/home-assistant";
    # specify list of components required by your configuration
    extraComponents = [
      "esphome"
      "met"
      "radio_browser"
    ];
  };
}
</syntaxhighlight>
 
=== Firewalling ===
Home Assistant by default listens on port 8123. It is customary to put services behind a [[Home Assistant#Reverse Proxying with nginx|reverse-proxy]], which allows sharing ports 80/443 (http/https) and offers TLS termination.
 
If you don't plan on using a reverse-proxy, you can allow unencrypted access by opening up the port home-assistant is running on in your firewall. This is not generally recommended, since credentials will be transmitted in plain text and browsers tend to limit certain functionality (e.g. geolocation) to HTTPS.
 
<syntaxhighlight lang="nix">
{
  config,
  ...
}:
 
{
  networking.firewall.allowedTCPPorts = [
    config.services.home-assistant.config.http.server_port
  ];
}
</syntaxhighlight>


===== First start =====
===== First start =====


On your first start you may see multiple <code><nowiki>ModuleNotFoundError</nowiki></code> in Home Assistants journal log. These are dependencies required to set up devices Home Assistant already found on the network.
On your first start you may see multiple <code><nowiki>ModuleNotFoundError</nowiki></code> in Home Assistants journal log. These are dependencies required to set up devices Home Assistant already discovered on the local network.


The appropriate component to load can be looked up in the <code><nowiki>component-packages.nix</nowiki></code> file, that gets auto-generated as part of the packaging process.
The appropriate component to load can be looked up in the <code>[https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/home-assistant/component-packages.nix component-packages.nix]</code> file, that gets auto-generated as part of the packaging process.


For example we can map the following error to
For example, we can map the following error to


   ModuleNotFoundError: No module named 'aioesphomeapi'
   ModuleNotFoundError: No module named 'aioesphomeapi'
Line 95: Line 153:
     [...]
     [...]
</syntaxHighlight>
</syntaxHighlight>
* https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/home-assistant/component-packages.nix


==== Using components without YAML configuration ====
==== Using components without YAML configuration ====


When a component has no YAML configuration its dependencies can in theory be installed by mentioning the component name in <code><nowiki>services.home-assistant.config.wled = {};</nowiki></code>. This is deprecated, since Home Assistant will usually complain about the config having been migrated into the graphical user interface.
When a component offers no YAML configuration its dependencies could in theory be installed by mentioning its component name in <code><nowiki>services.home-assistant.config.wled = {};</nowiki></code>. This is deprecated, since Home Assistant will usually complain about the config having been migrated into the graphical user interface.


In recent versions of the home-assistant this use case has become more prominent and therefore received a more straightforward implementation, that also ensures that the component is still provided by Home Assistant.
In recent versions of the home-assistant this use case has become more prominent and therefore received a more straightforward implementation, that also ensures that the component is still provided by Home Assistant.
Line 112: Line 168:
</syntaxHighlight>
</syntaxHighlight>


==== Making additional python packages available ====
==== Provide additional Python packages to Home Assistant ====


We control the dependencies we pass into the Home Assistant python environment through module options that make the dependencies available, when their relative component was declaratively mentioned.
We control the dependencies we pass into the Home Assistant python environment through module options that make the dependencies available, when their relative component was declaratively mentioned.
Line 133: Line 189:
==== Using custom components ====
==== Using custom components ====


We currently do not provide a way to declaratively manage custom components through the NixOS module. There is an open PR with an implementation, that still requires further work and discussion to make it a good solution.
We provide a way to declaratively manage custom components through the NixOS module with the [https://search.nixos.org/options?channel=unstable&show=services.home-assistant.customComponents&from=0&size=50&sort=relevance&type=packages&query=home-assistant services.home-assistant.customComponents] option.
 
* https://github.com/NixOS/nixpkgs/pull/160346
 
Until then custom components can be installed to <code><nowiki>/var/lib/hass/custom_components/</nowiki></code> as is the customary for Home Assistant.


Custom components can be found under [https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=home-assistant-custom-components pkgs.home-assistant-custom-components].


==== Using custom lovelace modules ====
==== Using custom lovelace modules ====


The support for custom lovelace modules is planned, same as for custom components.
We provide a way to declaratively manage custom lovelace modules through the NixOS module with the [https://search.nixos.org/options?channel=unstable&show=services.home-assistant.customLovelaceModules&from=0&size=50&sort=relevance&type=packages&query=home-assistant services.home-assistant.customLovelaceModules] option.
 
* https://github.com/NixOS/nixpkgs/pull/160346
 
Until then lovelace modules can be installed to <code><nowiki>/var/lib/hass/www/</nowiki></code>, which uses Home Assistants internal webserver to serve files below <code><nowiki>/local/</nowiki></code> on your installation. This webserver will not follow symlinks, which we will allow in the future by applying a patch, which will be reasonable safe to do because of all the hardening options we apply by default. The patch in the question is part of the PR above:
 
* https://raw.githubusercontent.com/mweinelt/nixpkgs/hass-custom-everything/pkgs/servers/home-assistant/patches/static-symlinks.patch
 
=== Reusing existing YAML configuration ===
 
The module also supports passing it an existing configuration, however that comes with certain drawbacks. For example we cannot automatically detect the components, that your configuration requires. In that scenario you will need to resolve dependencies manually using the packages <code>extraComponents</code> parameter. Also you will be unable to reuse configuration values between parts of your NixOS configuration. A barebones setup to get you started may look like this:
 
<syntaxHighlight lang=nix>
{
  services.home-assistant = {
    enable = true;
    # Pass the path to the directory where your configuration.yaml
    # resides, /var/lib/hass might be a good location.
    configDir = /var/lib/hass;
    # Override the package to handle dependency management manually
    package = (pkgs.home-assistant.override {
      # https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/home-assistant/component-packages.nix
      extraComponents = [
        "default_config"
        "esphome"
        "met"
      ];
      extraPackages = py: with py; [
        # Are you using a database server for your recorder?
        # https://www.home-assistant.io/integrations/recorder/
        #mysqlclient
        #psycopg2
 
      ];
    }).overrideAttrs (oldAttrs: {
      # Don't run package tests, they take a long time
      doInstallCheck = false;
    });
  };
}
</syntaxHighlight>


You may find the following script helpful. It looks up missing dependencies from the <code>home-assistant.service</code> systemd unit journal: https://gist.github.com/AngryAnt/74c047a2b8438517c822ffdd9663aa57
Custom components can be found under [https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=home-assistant-custom-lovelace-modules pkgs.home-assistant-custom-lovelace-modules].


= Running a recent version using an overlay =  
= Tracking the latest release =  


Home Assistant is a fast-paced open source project, that currently features one major release every month, and a handful of minor ones in between. Firmwares and API endpoints tend to change from time to  time, so Home Assistant and its bindings need to keep up to keep things work. The version we provide at the branch off is just a snapshot in time, and does not receive any updates, because there would just be too many dependencies to backport. But with NixOS it is still possible to use the version in nixpkgs/unstable by creating an overlay and using the module from nixos-unstable.
Home Assistant is a fast-paced open source project, that currently features one major release every month, and a handful of minor ones in between. Firmwares and API endpoints tend to change from time to  time, so Home Assistant and its bindings need to keep up to keep things work. The version we provide at the branch off is just a snapshot in time, and does not receive any updates, because there would just be too many dependencies and breaking changes to backport. But with NixOS it is still possible to use the version in nixpkgs/unstable by creating an overlay and using the module from [[Channel branches|nixos-unstable]].


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
Line 248: Line 261:
Home Assistant supports PostgreSQL as a database backend for, among other things, its logger and history components. It's a lot more scalable and typically provides faster response times than the SQLite database, that is used by default.
Home Assistant supports PostgreSQL as a database backend for, among other things, its logger and history components. It's a lot more scalable and typically provides faster response times than the SQLite database, that is used by default.


Remember to make backups of your database, for Home Assistant is becoming more and more stateful and has moved away from a completely declarative YAML configuration for new and core components.
<syntaxhighlight lang="nix">
 
Also note that when overridding the package you may want to disable install checks as they tend to take a long time to complete.
 
<syntaxHighlight lang=nix>
   services.home-assistant = {
   services.home-assistant = {
     package = (pkgs.home-assistant.override {
     extraPackages = ps: with ps; [ psycopg2 ];
      extraPackages = py: with py; [ psycopg2 ];
    }).overrideAttrs (oldAttrs: {
      doInstallCheck = false;
    });
     config.recorder.db_url = "postgresql://@/hass";
     config.recorder.db_url = "postgresql://@/hass";
   };
   };
Line 267: Line 272:
     ensureUsers = [{
     ensureUsers = [{
       name = "hass";
       name = "hass";
       ensurePermissions = {
       ensureDBOwnership = true;
        "DATABASE hass" = "ALL PRIVILEGES";
      };
     }];
     }];
   };
   };
</syntaxHighlight>
</syntaxhighlight>


== Add custom lovelace modules ==
== Updating Zigbee Firmware over the air ==


There are many useful and pretty lovelace components out there that you might want to integrate into your dashboards. Some of them are packaged up inside NUR repositories.
To allow ZHA OTA updates you need to configure the z2m_remote_index setting for ZHA. Before doing any updates, you should read the official integration documentation https://www.home-assistant.io/integrations/zha/#ota-firmware-updates


Usually you would install these into <code>/var/lib/hass/www</code>, but that comes with issues on NixOS, as their internal webserver does not follow symlinks. You could then think about exposing that directory over your webserver, but you'll notice that your webserver user has no permissions to enter the home assistant users state directory, which is due to proper hardening on the <code>home-assistant.service</code>.
Before updating a device, you should do some research. Some firmware updates break certain features you might use (e.g., group binding for IKEA devices). Some updates may also require changes to ZHA. In rare cases, you can even brick devices by installing a firmware update.


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
let
services.home-assistant.config = {
  # https://nur.nix-community.org/repos/mweinelt/
   zha.zigpy_config.ota.z2m_remote_index = "https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/index.json";
   nur = import (builtins.fetchTarball "https://github.com/mweinelt/nur-packages/archive/master.tar.gz") {};
};
</syntaxHighlight>
 
== Automations, Scenes, and Scripts from the UI ==


  mkLovelaceModule = name: {
These can be created from the user interface, but the files generated from it need to be included in your configuration.<syntaxhighlight lang="nixos">
    # https://www.home-assistant.io/lovelace/dashboards-and-views/#resources
{
     url = "/local/${name}.js?${nur.hassLovelaceModules."${name}".version}";
  services.home-assistant.config = {
     type = "module";
     "automation ui" = "!include automations.yaml";
    "scene ui" = "!include scenes.yaml";
     "script ui" = "!include scripts.yaml";
   };
   };
in {
};
  # Install lovelace components into temporary directory that can be
</syntaxhighlight>It is also possible to mix declarative and generated configuration for these components, by creating multiple configuration sections with the automation, scenes, or scripts prefix:
  # served by nginx.
<syntaxhighlight lang="nix">
  systemd.tmpfiles.rules = [
services.home-assistant.config = {
    "d /run/hass 0700 nginx nginx"
  "automation nixos" = [
    "L+ /run/hass/mini-graph-card.js - - - - ${nur.hassLovelaceModules.mini-graph-card}/mini-graph-card-bundle.js"
     # YAML automations go here
    "L+ /run/hass/mini-media-player.js - - - - ${nur.hassLovelaceModules.mini-media-player}/mini-media-player-bundle.js"
     "L+ /run/hass/multiple-entity-row.js - - - - ${nur.hassLovelaceModules.multiple-entity-row}/multiple-entity-row.js"
   ];
   ];
  "automation ui" = "!include automations.yaml";
}
</syntaxhighlight>
= Examples =


  # Instruct home-assistant to load these resources in the lovelace frontend
=== Entity Customization ===
  services.home-assistant.config.lovelace = {
    resources = [
      (mkLovelaceModule "mini-graph-card") # https://github.com/kalkih/mini-graph-card
      (mkLovelaceModule "mini-media-player") # https://github.com/kalkih/mini-media-player
      (mkLovelaceModule "multiple-entity-row") # https://github.com/benct/lovelace-multiple-entity-row
    ];
  };


  services.nginx.virtualHosts."home.example.com" = {
You can declaratively define how entities appear in the GUI with respect to their display names (friendly_name) the "show as" (<code>device_class</code>) and the icon displayed (<code>icon</code>). See this page for more documentation and how the YAML will ultimately be generated: https://www.home-assistant.io/docs/configuration/customizing-devices/. <syntaxhighlight lang="nix">
    locations."/local/" = {
    config = {
      alias = "/run/hass/";
      default_config = {};
      homeassistant = {
        # MUST be at the top or will break entire configuration
        customize = {
          # Declare all "entity_id" objects here at this level to customize them
          "binary_sensor.name" = {
            # Custom name however you want the entity to appear in the GUI
            friendly_name = "friendlyname";
            # See https://www.home-assistant.io/integrations/binary_sensor/ for documentation
            device_class = "deviceclass";
            # See https://www.home-assistant.io/docs/configuration/customizing-devices/#icon for documentation
            icon = "mdi:iconname";
          };
        };
      };
     };
     };
  };
}


</syntaxHighlight>
</syntaxhighlight>
 
=== Alarm Control Panel ===
You can declaratively define your own Alarm Control Panel which will appear on the GUI and have entities available to be changed via declaratively created automations. See https://www.home-assistant.io/integrations/manual/ for more documentation. <syntaxhighlight lang="nix">
    config = {
      default_config = {};
      homeassistant = {
 
      # On same level as automations
      "alarm_control_panel" = [
        {
          platform = "manual";
          name = "Home Alarm";
          code_arm_required = "false";
          arming_time = "30";
          delay_time = "20";
          trigger_time = "4";
          disarmed = {
            trigger_time = "0";
          };
          armed_home = {
            arming_time = "0";
            delay_time = "0";
          };
          armed_night = {
            arming_time = "0";
            delay_time = "0";
          };
        }
      ];
 
 
</syntaxhighlight>
 
=== Groups / Helpers ===
You can declaratively define groups rather than setting them up in the GUI, and customize their unique_id, platform, type, and entitiy_id's associated. See https://www.home-assistant.io/integrations/group/ for more documentation. Can be used in conjunction with “Entity Customization” section above for additional flexibility by plugging in the <code>unique_id</code> then changing the <code>friendly_name</code>, <code>icon</code>, <code>device_class</code> etc.
 
==== Binary Sensor Group ====
Example of Door and Window Sensor Group that could be used in an Automation for triggering an alarm system if any door or window is opened.<syntaxhighlight lang="nix">
      # Door and Window Sensor Group
      "binary_sensor" = [
        {
          unique_id = "binary_sensor.all_door_and_window_sensors";
          platform = "group";
          device_class = "door";
          entities = [
            "binary_sensor.sensor_1"
            "binary_sensor.sensor_2"
            "binary_sensor.sensor_3"
          ];
        }
      ];
 
</syntaxhighlight>
 
==== Sensor Group ====
Example of Sensor group using “min” mode that could be used in an Automation to trigger a Low Battery Alert across all batteries in the group.<syntaxhighlight lang="nix">
      # Sensor Battery Group
      "sensor" = [
        {
          unique_id = "sensor.all_batteries";
          platform = "group";
          type = "min";
          # Use this or else if any go to "unknown" the group will show unknown
          ignore_non_numeric = "true";
          device_class = "battery";
          entities = [
            "sensor.battery_1"
            "sensor.battery_2"
            "sensor.battery_3"
          ];
        }
      ];
 
</syntaxhighlight>
 
=== Automations ===
 
==== Automation with a Condition ====
<syntaxhighlight lang="nix">
        {
          alias = "Name To Display in Automations List";
          trigger = {
            platform = "state";
            entity_id = "binary_sensor.someid1";
            to = "off";
            for = "00:10:00";
          };
          condition = {
            condition = "state";
            entity_id = "binary_sensor.someid2";
            state = "on";
          };
          action = {
            service = "light.turn_off";
            entity_id = "light.someid";
          };
        }
</syntaxhighlight>
 
==== Automation with Multiple Conditions, Multiple Actions ====
<syntaxhighlight lang="nix">
        {
          alias = "Name in Automations GUI";
          trigger = {
            platform = "state";
            entity_id = "binary_sensor.someid";
            to = "on";
          };
          condition = [
            {
              condition = "state";
              entity_id = "sun.sun";
              state = "below_horizon";
            }
            {
              condition = "state";
              entity_id = "light.someid";
              state = "off";
            }
          ];
          action = [
            {
              service = "notify.notify";
              data = {
                message = "Some Notification";
              };
            }
            {
              service = "siren.turn_on";
              entity_id = "siren.someid";
            }
          ];
        }
 
</syntaxhighlight>
 
==== Trigger Using Numeric State ====
<syntaxhighlight lang="nix">
        {
          alias = "Some Name";
          trigger = {
            platform = "numeric_state";
            entity_id = "sensor.batteries";
            below = "45";
          };
          action = {
            service = "notify.notify";
            data = {
              message = "Low Battery Detected";
            };
          };
        }
</syntaxhighlight>
 
==== Trigger Checking for Entity State Missing / Changing to Unknown ====
<syntaxhighlight lang="nix">
        {
          alias = "Object Went Unknown";
          trigger = {
            platform = "state";
            entity_id = "switch.someid";
            to = "unknown";
            for = "00:5:00";
          };
          action = {
            service = "notify.notify";
            data = {
              message = "Object Went Offline";
            };
          };
        }
 
</syntaxhighlight>


== Add custom components ==
==== Time Based Trigger, Setting Data Field On Entity Such as Thermostat ====
<syntaxhighlight lang="nix">
        {
          alias = "Do Something At Certain Time";
          trigger = {
            platform = "time";
            at = "23:00:00";
          };
          action = {
            service = "climate.set_temperature";
            entity_id = "climate.thermostat";
            data = {
              temperature = "68";
            };
          };
        }


In order to install a custom component, you have to place it in <code>/var/lib/hass/custom_components</code>. This can be achieved using systemd tmpfiles like so (for sonoff custom component):
</syntaxhighlight>If you did not create any automations through the UI, Home Assistant will fail loading because the <code>automations.yaml</code> file does not exist yet, and it will fail including it. To avoid that, add a systemd tmpfiles.d rule:


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
  systemd.tmpfiles.rules = [
systemd.tmpfiles.rules = [
    "C /var/lib/hass/custom_components/sonoff - - - - ${sources.sonoff-lan}/custom_components/sonoff"
  "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass"
    "Z /var/lib/hass/custom_components 770 hass hass - -"
];
  ];
</syntaxHighlight>
</syntaxHighlight>


== Combine declarative and UI defined automations ==
== Trust a private certificate authority ==
 
Home Assistant does not natively support adding a private CA to the certificate store (see [https://community.home-assistant.io/t/add-private-cas-to-certificate-store/267452 this thread] for more details).
 
Home Assistant trusts certificates provided by the <code>certifi</code> python package, which nix overwrites with the <code>cacert</code> package.  Using overrides you can append your root CA certificate to the certificates provided by <code>certifi</code>.


You can also declaratively define your automations while still being able to define them in Home Assistant UI. Automations defined in UI are stored in <code>/var/lib/hass/automations.yaml</code>. If you are not planning on using UI defined automations, then you can define them under  <code>services.home-assistant.config.automation</code>, otherwise split them into <code>services.home-assistant.config."automation manual"</code> and <code>services.home-assistant.config."automation ui"</code>, like so:
<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
   services.home-assistant.config =
   services.home-assistant.package = (pkgs.home-assistant.override {
  {
     extraPackages = py: with py; [ ];
     "automation manual" = [
    packageOverrides = final: prev: {
      {
      certifi = prev.certifi.override {
        alias = "living room plug off";
         cacert = pkgs.cacert.override {
         trigger = {
           extraCertificateFiles = [ ./my_custom_root_ca.crt ];
           platform = "time";
          at = "22:00";
         };
         };
        action = {
      };
          type = "turn_off";
  }).overrideAttrs (oldAttrs: {
          device_id = "someID"; #Inspect yaml of automation created in UI
     doInstallCheck = false;
          entity_id = "switch.living_room_plug";
  });
          domain = "switch";
        };
      }
     "automation ui" = "!include automations.yaml";
}
</syntaxHighlight>
 
== Combine declarative and UI defined scenes ==
Scenes are configured the same way automations(described above) are.
<syntaxHighlight lang=nix>
services.home-assistant.config."scene manual" = []
services.home-assistant.config."scene ui" = "!include scenes.yaml";
</syntaxHighlight>
</syntaxHighlight>


= Example configurations =
= Example configurations =


- [https://github.com/Mic92/dotfiles/tree/master/nixos/eve/modules/home-assistant Mic92's config]
* [https://github.com/Mic92/dotfiles/tree/main/nixos/eve/modules/home-assistant Mic92's config]


= Misc =
= Misc =
Line 369: Line 561:


When developing Home Assistant for some test dependencies additional libraries are needed.
When developing Home Assistant for some test dependencies additional libraries are needed.
A nix-shell expression for this is available [https://github.com/nix-community/nix-environments here].
A nix-shell expression for this is available [https://github.com/nix-community/nix-environments/tree/master/envs/home-assistant here].


[[Category:Applications]]
[[Category:Applications]]

Latest revision as of 22:30, 20 December 2024

Home Assistant is an open source home automation software that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts.

NixOS provides native support for Home Assistant Core and offers integration facilities for most pieces of its comprehensive ecosystem:

Support

Depending on the installation method one of various support channels should be used.

If you rely on the NixOS package and/or module, issues should be reported on the nixpkgs Issue tracker or the #homeautomation:nixos.org Matrix room.

Only if you rely on one of the upstream supported deployment methods issues can be directly reported to the upstream project. Make sure to follow their guide on reporting issues.

Upstream installation methods

If you intend for Home Assistant to be an end-user configurable experience, as opposed to the declarative configuration experience NixOS offers, then consider these setups. They have the benefit of full upstream support.

Virtual machine

Home Assistant OS in Virtualbox
HAOS in Virtualbox

Home Assistant maintains their own operating system and provides QEMU (qcow2) and Virtualbox (vdi) compatible images. NixOS supports virtualization solutions like libvirt and Incus, both of which wrap QEMU, and Virtualbox.

Example:

OCI container

Home Assistant also provides a container image for OCI compatible runtimes.

The following example configuration uses podman to download and run the home-assistant:stable image. The frontend will be available via HTTP on port tcp/8123 in the host network namespace and can be reverse proxied from there.

{  
  virtualisation.oci-containers = {
    backend = "podman";
    containers.homeassistant = {
      volumes = [ "home-assistant:/config" ];
      environment.TZ = "Europe/Berlin";
      # Note: The image will not be updated on rebuilds, unless the version label changes
      image = "ghcr.io/home-assistant/home-assistant:stable";
      extraOptions = [ 
        # Use the host network namespace for all sockets
        "--network=host"
        # Pass devices into the container, so Home Assistant can discover and make use of them
        "--device=/dev/ttyACM0:/dev/ttyACM0"
      ];
    };
  };
}

Native installation

Declarative configuration

Set up your home-assistant by configuring the services.home-assistant.config attribute set as if it were your home-assistant YAML configuration. The module parses the root and platforms level to automatically discover integrations used and will provide their dependencies to your home-assistant package.

The following is a minimal configuration, that has all the dependencies that are required to complete the initial configuration flow, that creates your first user:

{
  services.home-assistant = {
    enable = true;
    extraComponents = [
      # Components required to complete the onboarding
      "analytics"
      "google_translate"
      "met"
      "radio_browser"
      "shopping_list"
      # Recommended for fast zlib compression
      # https://www.home-assistant.io/integrations/isal
      "isal"
    ];
    config = {
      # Includes dependencies for a basic setup
      # https://www.home-assistant.io/integrations/default_config/
      default_config = {};
    };
  };
}

Imperative configuration

Alternatively, If you would like to manage your configuration outside your NixOS configuration, you can set up the module to pass a configuration directory. This kind of setup is useful if you want to gradually migrate your existing configuration over.

Using a custom configuration has the drawback, that we cannot automatically recognize and install component dependencies, and it is unlikely that we will continue to support these kinds of setups going forward.

{
  services.home-assistant = {
    # opt-out from declarative configuration management
    config = null;
    lovelaceConfig = null;
    # configure the path to your config directory
    configDir = "/etc/home-assistant";
    # specify list of components required by your configuration
    extraComponents = [
      "esphome"
      "met"
      "radio_browser"
    ];
  };
}

Firewalling

Home Assistant by default listens on port 8123. It is customary to put services behind a reverse-proxy, which allows sharing ports 80/443 (http/https) and offers TLS termination.

If you don't plan on using a reverse-proxy, you can allow unencrypted access by opening up the port home-assistant is running on in your firewall. This is not generally recommended, since credentials will be transmitted in plain text and browsers tend to limit certain functionality (e.g. geolocation) to HTTPS.

{
  config,
  ...
}:

{
  networking.firewall.allowedTCPPorts = [
    config.services.home-assistant.config.http.server_port
  ];
}
First start

On your first start you may see multiple ModuleNotFoundError in Home Assistants journal log. These are dependencies required to set up devices Home Assistant already discovered on the local network.

The appropriate component to load can be looked up in the component-packages.nix file, that gets auto-generated as part of the packaging process.

For example, we can map the following error to

 ModuleNotFoundError: No module named 'aioesphomeapi'

the esphome module quite easily.

{
  version = "2022.8.0";
  components = {
    [...]
    "esphome" = ps: with ps; [
      aioesphomeapi
      aiohttp-cors
      ifaddr
      zeroconf
    ];
    [...]

Using components without YAML configuration

When a component offers no YAML configuration its dependencies could in theory be installed by mentioning its component name in services.home-assistant.config.wled = {};. This is deprecated, since Home Assistant will usually complain about the config having been migrated into the graphical user interface.

In recent versions of the home-assistant this use case has become more prominent and therefore received a more straightforward implementation, that also ensures that the component is still provided by Home Assistant.

{
  services.home-assistant.extraComponents = [
    "wled"
  ];
}

Provide additional Python packages to Home Assistant

We control the dependencies we pass into the Home Assistant python environment through module options that make the dependencies available, when their relative component was declaratively mentioned.

For other use cases like PostgreSQL support in the recorder component or the use of custom components, we provide an option to inject arbitrary dependencies from nixpkgs available python package set.

{
  services.home-assistant.extraPackages = python3Packages: with python3Packages; [
    # recorder postgresql support
    psycopg2

    # miele@home
    flatdict
    (callPackage ./pymiele.nix)
  ];
}

Using custom components

We provide a way to declaratively manage custom components through the NixOS module with the services.home-assistant.customComponents option.

Custom components can be found under pkgs.home-assistant-custom-components.

Using custom lovelace modules

We provide a way to declaratively manage custom lovelace modules through the NixOS module with the services.home-assistant.customLovelaceModules option.

Custom components can be found under pkgs.home-assistant-custom-lovelace-modules.

Tracking the latest release

Home Assistant is a fast-paced open source project, that currently features one major release every month, and a handful of minor ones in between. Firmwares and API endpoints tend to change from time to time, so Home Assistant and its bindings need to keep up to keep things work. The version we provide at the branch off is just a snapshot in time, and does not receive any updates, because there would just be too many dependencies and breaking changes to backport. But with NixOS it is still possible to use the version in nixpkgs/unstable by creating an overlay and using the module from nixos-unstable.

let
  # Track NixOS unstable via nix-channel, or replace it with something like niv at your own discretion
  # nix-channel --add http://nixos.org/channels/nixos-unstable nixos-unstable
  unstable = import <nixos-unstable> {};
in
{
  nixpkgs.overlays = [
    (self: super: {
      inherit (unstable) home-assistant;
    })
  ];

  disabledModules = [
    "services/home-automation/home-assistant.nix"
  ];

  imports = [
    <nixos-unstable/nixos/modules/services/home-automation/home-assistant.nix>
  ];
}

Snippets

Reverse Proxying with nginx

If you run a public Home Assistant instance it is a good idea to enable SSL/TLS. The following configuration generates a certificate using letsencrypt:

  services.home-assistant.config.http = {
    server_host = "::1";
    trusted_proxies = [ "::1" ];
    use_x_forwarded_for = true;
  };

  services.nginx = {
    recommendedProxySettings = true;
    virtualHosts."home.example.com" = {
      forceSSL = true;
      enableACME = true;
      extraConfig = ''
        proxy_buffering off;
      '';
      locations."/" = {
        proxyPass = "http://[::1]:8123";
        proxyWebsockets = true;
      };
    };
  };

Using PostgreSQL

Home Assistant supports PostgreSQL as a database backend for, among other things, its logger and history components. It's a lot more scalable and typically provides faster response times than the SQLite database, that is used by default.

  services.home-assistant = {
    extraPackages = ps: with ps; [ psycopg2 ];
    config.recorder.db_url = "postgresql://@/hass";
  };

  services.postgresql = {
    enable = true;
    ensureDatabases = [ "hass" ];
    ensureUsers = [{
      name = "hass";
      ensureDBOwnership = true;
    }];
  };

Updating Zigbee Firmware over the air

To allow ZHA OTA updates you need to configure the z2m_remote_index setting for ZHA. Before doing any updates, you should read the official integration documentation https://www.home-assistant.io/integrations/zha/#ota-firmware-updates

Before updating a device, you should do some research. Some firmware updates break certain features you might use (e.g., group binding for IKEA devices). Some updates may also require changes to ZHA. In rare cases, you can even brick devices by installing a firmware update.

services.home-assistant.config = {
  zha.zigpy_config.ota.z2m_remote_index = "https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/index.json";
};

Automations, Scenes, and Scripts from the UI

These can be created from the user interface, but the files generated from it need to be included in your configuration.

{
  services.home-assistant.config = {
    "automation ui" = "!include automations.yaml";
    "scene ui" = "!include scenes.yaml";
    "script ui" = "!include scripts.yaml";
  };
};

It is also possible to mix declarative and generated configuration for these components, by creating multiple configuration sections with the automation, scenes, or scripts prefix:

services.home-assistant.config = {
  "automation nixos" = [
    # YAML automations go here
  ];
  "automation ui" = "!include automations.yaml";
}

Examples

Entity Customization

You can declaratively define how entities appear in the GUI with respect to their display names (friendly_name) the "show as" (device_class) and the icon displayed (icon). See this page for more documentation and how the YAML will ultimately be generated: https://www.home-assistant.io/docs/configuration/customizing-devices/.

    config = {
      default_config = {};
      homeassistant = {
        # MUST be at the top or will break entire configuration
        customize = {
          # Declare all "entity_id" objects here at this level to customize them
          "binary_sensor.name" = {
            # Custom name however you want the entity to appear in the GUI
            friendly_name = "friendlyname";
            # See https://www.home-assistant.io/integrations/binary_sensor/ for documentation
            device_class = "deviceclass"; 
            # See https://www.home-assistant.io/docs/configuration/customizing-devices/#icon for documentation
            icon = "mdi:iconname";
          };
        };
      };
    };

Alarm Control Panel

You can declaratively define your own Alarm Control Panel which will appear on the GUI and have entities available to be changed via declaratively created automations. See https://www.home-assistant.io/integrations/manual/ for more documentation.

    config = {
      default_config = {};
      homeassistant = {

      # On same level as automations
      "alarm_control_panel" = [
        {
          platform = "manual";
          name = "Home Alarm";
          code_arm_required = "false";
          arming_time = "30";
          delay_time = "20";
          trigger_time = "4";
          disarmed = {
            trigger_time = "0";
          };
          armed_home = {
            arming_time = "0";
            delay_time = "0";
          };
          armed_night = {
            arming_time = "0";
            delay_time = "0";
          };
        }
      ];

Groups / Helpers

You can declaratively define groups rather than setting them up in the GUI, and customize their unique_id, platform, type, and entitiy_id's associated. See https://www.home-assistant.io/integrations/group/ for more documentation. Can be used in conjunction with “Entity Customization” section above for additional flexibility by plugging in the unique_id then changing the friendly_name, icon, device_class etc.

Binary Sensor Group

Example of Door and Window Sensor Group that could be used in an Automation for triggering an alarm system if any door or window is opened.

      # Door and Window Sensor Group
      "binary_sensor" = [
        {
          unique_id = "binary_sensor.all_door_and_window_sensors";
          platform = "group";
          device_class = "door";
          entities = [
            "binary_sensor.sensor_1"
            "binary_sensor.sensor_2"
            "binary_sensor.sensor_3"
          ];
        }
      ];

Sensor Group

Example of Sensor group using “min” mode that could be used in an Automation to trigger a Low Battery Alert across all batteries in the group.

      # Sensor Battery Group
      "sensor" = [
        {
          unique_id = "sensor.all_batteries";
          platform = "group";
          type = "min";
          # Use this or else if any go to "unknown" the group will show unknown
          ignore_non_numeric = "true";
          device_class = "battery";
          entities = [
            "sensor.battery_1"
            "sensor.battery_2"
            "sensor.battery_3"
          ];
        }
      ];

Automations

Automation with a Condition

        {
          alias = "Name To Display in Automations List";
          trigger = {
            platform = "state";
            entity_id = "binary_sensor.someid1";
            to = "off";
            for = "00:10:00";
          };
          condition = {
            condition = "state";
            entity_id = "binary_sensor.someid2";
            state = "on";
          };
          action = {
            service = "light.turn_off";
            entity_id = "light.someid";
          };
        }

Automation with Multiple Conditions, Multiple Actions

        {
          alias = "Name in Automations GUI";
          trigger = {
            platform = "state";
            entity_id = "binary_sensor.someid";
            to = "on";
          };
          condition = [
            {
              condition = "state";
              entity_id = "sun.sun";
              state = "below_horizon";
            }
            {
              condition = "state";
              entity_id = "light.someid";
              state = "off";
            }
          ];
          action = [
            {
              service = "notify.notify";
              data = {
                message = "Some Notification";
              };
            }
            {
              service = "siren.turn_on";
              entity_id = "siren.someid";
            }
          ];
        }

Trigger Using Numeric State

        {
          alias = "Some Name";
          trigger = {
            platform = "numeric_state";
            entity_id = "sensor.batteries";
            below = "45";
          };
          action = {
            service = "notify.notify";
            data = {
              message = "Low Battery Detected";
            };
          };
        }

Trigger Checking for Entity State Missing / Changing to Unknown

        {
          alias = "Object Went Unknown";
          trigger = {
            platform = "state";
            entity_id = "switch.someid";
            to = "unknown";
            for = "00:5:00";
          };
          action = {
            service = "notify.notify";
            data = {
              message = "Object Went Offline";
            };
          };
        }

Time Based Trigger, Setting Data Field On Entity Such as Thermostat

        {
          alias = "Do Something At Certain Time";
          trigger = {
            platform = "time";
            at = "23:00:00";
          };
          action = {
            service = "climate.set_temperature";
            entity_id = "climate.thermostat";
            data = {
              temperature = "68";
            };
          };
        }

If you did not create any automations through the UI, Home Assistant will fail loading because the automations.yaml file does not exist yet, and it will fail including it. To avoid that, add a systemd tmpfiles.d rule:

systemd.tmpfiles.rules = [
  "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass"
];

Trust a private certificate authority

Home Assistant does not natively support adding a private CA to the certificate store (see this thread for more details).

Home Assistant trusts certificates provided by the certifi python package, which nix overwrites with the cacert package. Using overrides you can append your root CA certificate to the certificates provided by certifi.

  services.home-assistant.package = (pkgs.home-assistant.override {
    extraPackages = py: with py; [ ];
    packageOverrides = final: prev: {
      certifi = prev.certifi.override {
        cacert = pkgs.cacert.override {
          extraCertificateFiles = [ ./my_custom_root_ca.crt ];
        };
      };
  }).overrideAttrs (oldAttrs: {
    doInstallCheck = false;
  });

Example configurations

Misc

Run Home Assistant from GitHub repository

When developing Home Assistant for some test dependencies additional libraries are needed. A nix-shell expression for this is available here.