home assistant tips and best practices

Seven Home Assistant Tips and Best Practices

This post may contain affiliate links. Please see the disclaimer for more information.

I’ve been really busy with other things this week, so I haven’t had much time to work on computing projects. So, I thought I’d write a more informational post including some tips and best practices for Home Assistant configuration. I’m also starting to think more about tidying up my own configuration, so some of these are todo list items for me!

Home Assistant is rapidly moving towards being less reliant on textual configuration, with more and more configuration being available via the GUI. However, Paulus has stated that the YAML configuration will remain an option. For the time being, there is still quite a bit that absolutely needs to be configured via YAML. These tips are mostly around organising your configuration well in order to manage it’s complexity as it grows. Let’s get into it…

1. Use the secrets.yaml file (for everything)

I’ve put this one first because I believe it’s the single most important thing you can do which will allow you to manage your configuration. For anyone that’s not familiar with this, the secrets.yaml file allows you to remove all the sensitive data from your configuration files and store it in a simple key value file:

my_super_secret: "hello123"
my_super_top_secret: "this is not really a secret"

When you want to use a secret in your config, just prefix the name with !secret:

password: !secret my_super_secret

This should be used wherever you have any marginally sensitive data. Things like passwords and API keys are a given. However, host names, IP addresses, names of family members, times/dates of events and locations should also be hidden.

Separating these secrets out from your main configuration allows you to more easily share you configuration without leaking personal data. It also allows you to back up to locations where the data may become public. Your home automation configuration is a very personal thing! You can leak significant information about the operation of your home and family members.

It seems to be a common misnomer that this makes your system somehow more secure. It doesn’t! There is no protection (such as encryption) applied to the secrets file on the disk of your HASS system. What this does do is help prevent accidental data leakage. It won’t protect your secrets in the case that someone breaks into your HASS server and gets access to your raw config files. However, in that case you already have major problems.

EXTRA TIP: You can also use secrets in ESPHome, using the same syntax.

2. Store your configuration in Git

This one follows directly on from the use of the secrets file and is really enabled by it. Once you have removed all the sensitive data from your configuration, it becomes really easy to share it.

One of the best ways to share your configuration is to create a Git repository of it and publish it online via GitHub or GitLab. This forms a handy offsite backup. You also get the benefit of being able to track the history of your configuration and easily revert changes if you need to. Git was designed for managing software and your HASS configuration is software.

This is something I actually need to get much better at. Although I’ve had my configuration published for some time, I tend to let it get out of date with the local changes I make. This negates a lot of the benefits and makes it a pain to update it in one big blob. If I kept it updated better this would be more incremental and easier.

I’m not going to go into a detailed run through of how to get your config into Git. The Home Assistant documentation has a wonderful page to get you started. I go even further and use git-crypt to encrypt my sensitive files, like secrets.yaml so that even they can be included (without anyone being able to read them).

3. Group related items with packages

There are several ways to structure your configuration. Lot’s of people start out using a single large configuration.yaml file. Later on you may decide that this is unwieldy and decide to split it up.

In this case your configuration gets split by what kind of configuration it is. For example, automations go in one file (or directory), sensors in another, scripts in another and so on. The problem is that you end up with related configuration being all over the place, scattered throughout different files or directories.

Packages aim to solve this problem, by allowing related configuration to live together regardless of type. This means that you can put the configuration for an integration and any logic relating to it together. For example, here is a snippet from my vacuum package, in which I have both the vacuum integration config and the automations relating to the vacuum:

neato:
  username: !secret neato_user
  password: !secret neato_password

automation:
  - alias: "Schedule vacuum Monday and Friday"
    trigger:
      - platform: time
        at: "09:10:00"
    condition:
      - condition: time
        weekday:
          - mon
          - fri
    action:
      - service: vacuum.neato_custom_cleaning
        data:
          entity_id: vacuum.marvin
          mode: 1
          category: 2

Shortly after starting with home assistant I started splitting up my configuration into directories based on configuration type. This served me well for a while, but over the last year or so I’ve been moving (slowly) towards using packages. Now all my new configuration goes into packages, but I still have a load of the older stuff to move over.

4. Use multiple triggers with and conditions

In my TV power saving automation from my last post I used multiple triggers with an and condition that checked the state of those triggers again. This is a generally useful pattern for anything where you want something to happen as soon as all relevant entities are in a given state.

For example, to run an automation when everyone goes out:

automation:
  - alias: Go to away mode
    trigger:
      - platform: state
        entity_id: device_tracker.person1
        to: "not_home"
      - platform: state
        entity_id: device_tracker.person2
        to: "not_home"
    condition:
      condition: and
      conditions:
        - condition: state
          entity_id: device_tracker.person1
          state: "not_home"
        - condition: state
          entity_id: device_tracker.person2
          state: "not_home"
    action:
      service: script.turn_on
      entity_id: script.away_mode

Of course, I could just use a group for that automaton. This pattern gets really useful where the states you want to check differ between entities – as in the TV example.

5. Use hass-cli for quick config checking and restart

When editing my config I prefer to use vim over SSH to the HASS server. I’ll usually also be using the Home Assistant GUI to debug and test things via the dev-tools. Switching pages in the UI every time I want to restart is a bit of a pain. This is especially annoying with the recent UI changes which put the config check and restart buttons underneath the (for me greyed out) settings for location, name, timezone, etc.

As such a quick way to check my config and restart is very useful. To this end I have this in my ~/.bashrc file on my Home Assistant server:

alias hass-restart="hass-cli service call homeassistant.check_config && hass-cli service call homeassistant.restart"

This relies on having hass-cli installed. The command first calls the homeassistant.check_config service and then calls the homeassistant.restart service. It would be better if hass-cli could return 0 or 1 depending on the state of the configuration check and so not call the next step. However, it seems like HASS also checks the config before restarting. Having the the config check there is still useful to alert you to an invalid config.

home assistant tips and best practices
HASS-CLI will alert you to an invalid config via a persistent notification

With this in place, all I need to do is temporarily pause my vim session (with Ctrl-Z), type hass-restart and wait for it to restart.

6. Use the trigger object for more general automations

When an automation has multiple triggers it is possible to customise its behaviour based on what entity triggered it. This is done via the trigger object, which is passed into the actions of the automation for use in templates.

Here is an example from my battery notifications:

- alias: Critical Battery Notification
    trigger:
      - platform: numeric_state
        entity_id: sensor.kitchen_hall_door_battery
        below: 5
      - platform: numeric_state
        entity_id: sensor.kitchen_button_battery
        below: 5
        # more triggers here
    action:
      service: notify.notify
      data_template:
        title: "Critical Battery Alert"
        message: "Device '{{ trigger.to_state.name }}' has CRITICAL battery: {{ trigger.to_state.state }}%."

As you can see this automation is triggered on one of several entity state changes (I shortened the list for reproduction here). The resulting notification however is customised to the particular entity which trigger the automation at that time.

7. Use entity ID lists in complex template sensors

Home Assistant has automatic state tracking logic used for determining when to update the state of a template sensor or binary sensor. However some templates, particularly those containing for loops are too tricky for it to work out. The workaround to this is to specify the entity IDs to track manually via the entity_id list.

home assistant tips and best practices
Even if your template works in the dev-tools it may not work in a sensor without specifying the entity IDs

For example:

sensor:
  - platform: template
    sensors:
      active_switches:
        friendly_name: Active Switches
        entity_id:
          - switch.tv
          - switch.kettle
        value_template: >-
          {%- for entity in states.switch -%}
            {% if entity.state == "on" %}{{ entity.entity_id }}, {% endif %}
          {%- endfor -%}

I’m not sure why the sensor above would be useful (it’s a contrived example). I haven’t actually had the need to use a template like this so far, but it’s good to know how to make it work just in case I need it.

Conclusion

These are just seven items that I could think of, but I hope that you will find them useful. I’m sure there are probably many more tips and best practices for Home Assistant configuration. Feel free to share yours in the comments and other feedback channels. If I get enough suggestions I may do a crowdsourced follow up to this article!

If you liked this post and want to see more, please consider subscribing to the mailing list (below) or the RSS feed. You can also follow me on Twitter. If you want to show your appreciation, feel free to buy me a coffee.

Simple password management with Bash and GPG

A while ago I was looking for a password management solution that I could trust, so of course being as paranoid as I am I decided to write my own. Now I’ve decided to share what I came up with.

My system takes the form of two bash functions which inhabit my .bashrc file. First is a password generation function:

# function to generate a new password and add it to the secure file
# also copies the new password to the clipboard
# clipboard is cleared automatically after 10 seconds
pwg()
{
    CB=$(xclip -selection clipboard -out)
    gpg --decrypt $HOME/secure.txt.gpg > $HOME/secure.txt
    PW=$(pwgen -n 12 1)
    echo "$PW   $1" >> $HOME/secure.txt
    gpg --encrypt --recipient $MY_EMAIL $HOME/secure.txt
    rm $HOME/secure.txt

    echo $PW | xclip -selection clipboard
    echo
    echo "New password copied to clipboard!"
    echo "You have 10 seconds..."
    sleep 10
    echo $CB | xclip -selection clipboard
}

This generates a 12 character alphanumeric password using pwgen and appends it to my password file along with a user supplied token (which is later used to retrieve the password. The password file is a tab separated file stored in the the home directory and encrypted with GPG. The password is copied to the clipboard with the xclip tool, where it stays for ten seconds before being wiped.

The second part of the system is a function to retrieve the password:

# function to retrieve a password from the secure file and copy it to the clipboard
# clipboard is cleared automatically after 10 seconds
pw()
{
    CB=$(xclip -selection clipboard -out)
    gpg --decrypt $HOME/secure.txt.gpg | grep $1 | cut -f1 | xclip -selection clipboard
    echo
    echo "Password copied to clipboard!"
    echo "You have 10 seconds..."
    sleep 10
    echo $CB | xclip -selection clipboard
}

This function grabs the password from the secure file and again copies it to the clipboard ready to be pasted to wherever it is needed. Again the clipboard is cleared after ten seconds to prevent passwords hanging around to long.

To use these function just place them in your .bashrc file along with a definition of the MY_EMAIL variable (to allow GPG to find your key) and then source the file (or restart bash). Obviously you’ll need GPG, pwgen and xclip which on Fedora can be installed with:

$ sudo yum install gnupg pwgen xclip

Usage is very simple, just run each function in a terminal with an identification token as the argument:

$ pwg test

<GPG PROMPTS FOR PASSPHRASE>

File `secure.txt.gpg` exists. Overwrite? (y/N) y

New password copied to clipboard!
You have 10 seconds...
[robert@riker ~]$ pw test

<GPG PROMPTS FOR PASSPHRASE>

Password copied to clipboard!
You have 10 seconds...

That’s it! Feel free to give it a try. Improvments are most welcome, so please post them in the comments section.

Using Android WebViews

Recently I’ve been working on show notes support for SwallowCatcher. Since most podcast feeds include their show notes in the feed as embedded HTML I decided to render this HTML to display the show notes. This turned out to be ridiculously simple thanks to Android’s WebView class. The API for rendering arbitrary HTML within your app is almost Pythonic in its simplicity. However, there are a couple of gotchas which caught me out, so I decided to cover them here.

I started out by creating a new Activity class and a layout XML file for it. The layout XML was taken almost directly from the Android WebView Tutorial page:

<?xml version="1.0" encoding="utf-8"?>
<WebView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/shownotes"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
/>

In my Activity class the XML is loaded as normal. We also need to import the WebView class and the URLEncoder class. Additionally, the UnsupportedEncodingException class is required:

import android.webkit.WebView;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;

Next, we need to encode the text in the correct format. Weirdly, WebView requires that the text is URL escaped, but without spaces converted to ‘+’ symbols. I used URLEncoder to encode my content then replaced the ‘+’ symbols with spaces. This probably isn’t perfect, but for my purposes it works.

String text = "<html><head></head><body>Content goes here!</body></html>";
text = URLEncoder.encode(text, "utf-8");
text = text.replace('+', ' ');

Finally, we find the WebView from the XML and tell it to load the string as its content:

WebView web = (WebView)findViewById(R.id.shownotes);
web.loadData(text, "text/html", "utf-8");

All the above is encased in a try..catch statement for UnsupportedEncodingException, just in case the URLEncoder has a fit. That’s pretty much it. In SwallowCatcher the ShowNotesActivity is 56 lines, including all the verbose Java imports and bootstrapping and loading the content from the database via ORMLite.

As a wise action hero (and ex-Jedi) once said, “I love it when a plan comes together”.

Quick QR Code Generation

QR Codes are a really easy way to share information between your desktop or laptop and a smartphone, which don’t have the privacy issues inherent in Google’s Chrome To Phone. The excellent FOSS Barcode Scanner app for Android makes scanning these codes and opening the scanned data a breeze. However, generating them on the desktop can be a bit of a pain. There are several websites, browser addons and APIs for generating these codes, but up until now I haven’t found anything really quick and easy.

That is until today, when I discovered the ‘qrencode’ utility. Qrencode does exactly what it says on the tin. It takes some text, encodes it as a QR Code and writes the result as a PNG file. It’s a simple command line tool, which opens up awesome scripting possibilities.

My use case is simply to send the URL of a web page that I’m viewing on my desktop to my phone, via QR Code. I found that qrencode was quite happy to write its output to stdout, which meant I could display the result directly using ImageMagick’s ‘display’ command, e.g:

$ qrencode http://blog.webworxshop.com -o - | display

You’ll notice that by default the QR Code is quite small. We can fix this by increasing the block size:

$ qrencode http://blog.webworxshop.com -s 10 -o - | display

Next, I wanted to automatically pull the text to encode from somewhere to save typing – the clipboard was an ideal candidate. Enter ‘xclip’. Xclip is a command line utility to read and write from/to the X system’s built in clipboard. I used bash’s backtick command substitution to grab text from the clipboard and encode it:

$ qrencode `xclip -o` -s 10 -o - | display

And there you have it, a simple one line command to generate and display a QR Code from the contents of the clipboard. I created a bash script containing the command and assigned it to a keyboard shortcut in Gnome (Ctrl-Shift-Q), so that sharing URLs to my phone is as simple as selecting the text in the location bar and hitting Ctrl-C followed by Ctrl-Shift-Q.

If you want to give this a try, you’ll need to install the utilities discussed, in Fedora these can be installed with:

$ sudo yum install qrencode xclip ImageMagick

Enjoy!

Unofficial Python Module of the Week #2: configobj

Welcome to our third instalment of interesting Python modules. Unfortunately I’m a bit late with this section this week – in fact its next week already! The fourth instalment should be along towards the end of the week thus catching me up.

Today we’re going to cover something which isn’t in the standard library, but is nonetheless very useful. The module is configobj which is used for reading from and writing to INI style configuration files files. A simple INI file is shown below:

item1 = value
item2 = value2

[ section1 ]
item1 = value

[[ subsection ]]
item1 = value

In the above we can see the simple use of items, values sections and subsection. Subsections can be nested down as far a you want, but I don’t think most applications will need many more than two or three levels.

Installation

As this module isn’t in the standard library, we need to install it. On most Linux distros it should be in the package repositories, for example on Fedora 14:

$ sudo yum install python-configobj

Windows and Mac users can install from PyPi by following the instructions on the homepage.

Basic Usage

Reading from a configuration file with configobj couldn’t really be any simpler:

import configobj
config = configob.ConfigObj(filename)
myoption = config['item1']
mysectionoption = config['section1']['item1']
mysubsectionoption = config['section1']['subsection']['item1']

Basically, all you need to do is open a ConfigObj object by passing it a filename, then you just read from it as if its a dictionary object. Sections and subsections appear as nested dictionaries. Writing to the file is just as simple:

import configobj
config = configob.ConfigObj(filename)
config['newoption'] = 'new stuff'
config.write()

No surprises here, you just write to it as if it were a dictionary. All you have to do it call the write() method when you’ve finished, in order to sync everything to disk.

That’s pretty much it for basic usage. There is much more you can do with configobj, including advanced stuff like validation of configuration files. Check out the documentation for more info.