Danblog

Medical Resident, Software Developer, Basketball Fan

Getting Started with Hass.io Add-on Development


Earlier this week, I published the Dropbox Sync add-on for Hass.io, which makes it easy to upload Hass.io snapshots to Dropbox. Dropbox Sync is the first add-on I've published, and although I've worked on local add-ons before, I learned a lot about the development and publishing process with this project. The documentation around add-on development is improving, but some aspects are still unclear, and development can be even smoother with a few additional tools from the Hass.io community. I thought it may be useful to share some things that I found useful.

Run Hass.io with Vagrant

If you want to develop for Hass.io with nothing but your local machine, I'd argue the easiest (and definitely most fun) way to do so is with this Vagrant box from the Hass.io community. The project is still marked as experimental, but I used this vagrant box from start to finish when developing and testing Dropbox Sync without any issues (note that this does require VirtualBox to be installed). Getting a Hass.io instance up and running is easy:

# clone the hassio-vagrant repo to a location of your choice
git clone https://github.com/hassio-addons/hassio-vagrant.git hassio
cd hassio

# v0.0.3 is the latest release as of this writing
git checkout v0.0.3

vagrant up hassio

That's it. Tell vagrant which network bridge to use, and in a few minutes you'll have Hass.io running on your machine within a Vagrant host (check the vagrant output to get the IP on which Hass.io is being served). This even comes with a few bonuses: a Portainer and Netdata instance. Portainer allows you to check your add-on logs directly and provides some nice controls over the containers running within vagrant. Netdata provides monitoring of system resource usage by your docker containers.

The cloned repository already includes addons, share, config, ssl, and backup directories that are mapped to the vagrant box. Just develop add-ons in the addons directory and the changes are reflected in the vagrant box.

To stop the vagrant box, run vagrant halt hassio, and vagrant destroy hassio will destroy the box, allowing it to be built from scratch again (this will destroy your config folder). Conveniently, everything in the Hass.io addons folder will persist.

Of course, if you're working on anything that involves hardware access, developing directly on a Raspberry Pi (or other machine running Hass.io) would likely be a better option than a virtual machine.

Use the Community Hass.io Build Environment

So you've worked on your add-on and built it locally, and now you're ready to build Docker images for multiple architectures and distribution. The docs have some information about using the official docker build engine versus running the docker build commands on your own. While the official build engine is an option, I found the Community Build Environment to be a more complete experience with better features that make things easy as you start to release subsequent versions.

Let's walk through the bash command I use to build Dropbox Sync Docker images to demonstrate how to use the build environment and some of its features:

docker run -it --rm --privileged --name ${ADDON_NAME} \
        -v ~/.docker:/root/.docker \
        -v "$(pwd)":/docker \
        hassioaddons/build-env:latest \
        --target ${ADDON_NAME} \
        --git \
        --all \
        --from "homeassistant/{arch}-base" \
        --author "Daniel Welch <dwelch2102@gmail.com>" \
        --doc-url "${GITHUB_URL}"
  • -v ~/.docker:/root/.docker gives our Docker container access to Docker Hub credentials, which I'll need if I'm going to push the image.
  • hassioaddons/build-env:latest tells Docker what image we're running, in this case the build environment image that will do the work for us.
  • --target specifies a sub-folder that contains the add-on Dockerfile, config.json, and run.sh.
  • --all tells the build script to build images for all architectures. If you want to specify individual architectures, you may do so with seperate flags.
  • --from defines a base image, where {arch} is expanded to a system architecture during the build. Alternatively, a seperate base image for each architecture can be chosen using seperate flags (ex: --aarch64-from).

The most useful feature of the community build environment is the --git flag, which automatically manages Docker image tagging and versioning for you. When you git tag a commit and then build images from that commit, the tag (ex: v0.1.0) is automatically added to the image, along with the :latest tag. If there are uncomitted changes in the repository, the :test tag is used.

Using the --git flag will override whatever you have set as the version in the config.json file of your add-on, and won't work if you have multiple add-ons in one repo. This poses a potential problem for those who keep and distribute all of their addons in one repository (understandable since Hass.io users install add-ons via adding github repository URLs). Thankfully, the Community Hass.io Add-ons repository itself provides a great example solution: develop add-ons in their own repository with an arbitrary version set in the config.json, and collect all add-ons you want to distribute in a single repository with configuration files for each add-on. Take a look at what that repository includes in each add-on subdirectory, but for this discussion, it's just important to note that the "aggregated repository" configuration file is what your users will actually load when installing your add-on, and therefore the version set in your repository is the version your users will be able to install.

Automated Builds and Deployment to Docker Hub with Travis CI

Here's where things get fun. We've seen how to build Docker images for multiple architectures with the Community Build Environment, and use the --git flag to automatically tag those images with versions. With a CI service like Travis CI, we can automate this entire process so that all we have to do is commit, tag and push our code to Github to trigger builds and upload them to Docker Hub. Let's look at how this is handled in Dropbox Sync:

# .travis.yml

sudo: required
services:
- docker
script:
- $TRAVIS_BUILD_DIR/build.sh
after_success:
- $TRAVIS_BUILD_DIR/distribute.sh
env:
  global:
    secure: <ENCRYPTED_STRING>  # encrypted docker hub password, see https://docs.travis-ci.com/user/environment-variables/#Defining-encrypted-variables-in-.travis.yml
# $TRAVIS_BUILD_DIR/build.sh

set -ev
docker run -it --rm --privileged --name ${ADDON_NAME} \
        -v ~/.docker:/root/.docker \
        -v "$(pwd)":/docker \
        hassioaddons/build-env:latest \
        --target ${ADDON_NAME} \
        --git \
        --all \
        --from "homeassistant/{arch}-base" \
        --author "Daniel Welch <dwelch2102@gmail.com>" \
        --doc-url "${GITHUB_URL}"
echo "Local Docker build successful."
# $TRAVIS_BUILD_DIR/distribute.sh

#!/bin/bash
set -ev

docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"
docker run -it --rm --privileged --name "${ADDON_NAME}" \
    -v ~/.docker:/root/.docker \
    -v "$(pwd)":/docker \
    hassioaddons/build-env:latest \
    --target "${ADDON_NAME}" \
    --git \
    --all \
    --push \
    --from "homeassistant/{arch}-base" \
    --author "Daniel Welch <dwelch2102@gmail.com>" \
    --doc-url "${GITHUB_URL}"

So when changes are pushed to Github, the add-on is built and, if successful, the build succeeds. A successful build triggers distribute.sh, which builds the images and pushes them to Docker Hub. The push location is determined by either the image key in your config.json, or the --image flag if used in the build script. Again, the {arch} variable is expanded to the respective architecture as usual. Be sure to create the Docker Hub repositories ahead of time.

With all of this in place, the development process is smooth:

  1. Write add-on code. Test how things work in your Hass.io vagrant box instance.
  2. Commit and push changes to Github, and get alerted as to whether or not the Travis build passes.
  3. When you're ready for a new release, create a new git tag and push it to Github. The image is tagged with the version and uploaded to Docker Hub automatically if the Travis build is successful.
  4. Lastly, to make the new version available within Hass.io, update the version in the config stub in the aggregated repository.