Developing an Add-On

Home Assistant is an open-source self-hosted home automation platform. It can be used to locally control a variety of different home smart devices.

There are few ways Home Assistant can be deployed. The method I currently use is Home Assistant Supervised. This runs the Home Assistant Core whilst also leveraging docker to deploy add-ons that provide additional standalone applications. Home Assistant allows custom add-ons to be written to bring extra functionally required by the user.


The problem

Home Assistant Supervised provides snapshot functionality that snapshots the current configuration of Home Assistant. These snapshots are stored locally as tar files. I needed a way to remove the unneeded older snapshots then transfer the remaining files to my network attached storage.


The aim

Produce a custom add-on that:

  1. removes snapshots older than a specified number of days

  2. pushes snapshots to a rsync server with and without support for authentication


The solution

The git repository of the project can be found here

Directory structure
rsync/
     Dockerfile
     config.json
     run.sh

Configuration for dockerfile build

The first task was to write config.json; this is the configuration required by the Home Assistant Supervisor to build the add-on using the supplied Dockerfile. Once built, the add-on is accessible through the Home Assistant web interface and allows for user configuration to be entered in json format. Required user configuration is generated by the options key below.

config.json

{ "name": "Remove and Rsync", "version": "1.0", "slug": "remove_backups_rsync", "description": "Remove old backups and rsync to server", "url": "https://gitlab.com/jselby/hassio-addons/blob/master/README.md", "arch": ["armhf", "armv7", "aarch64", "amd64", "i386"], "startup": "once", "boot": "manual", "options": { "keep_days":7, "server_address":"", "destination_directory":"", "username":"", "password":"" }, "schema": { "keep_days":"int", "server_address":"str", "destination_directory":"str", "username":"str", "password":"str" }, "map": ["backup:rw"] }

config.json explained

Key Type Description
name string Name of the add-on
version string Version of the add-on
slug String Slug of the add-on; unique to repository add-on resides; url friendly
description string Description of the add-on
url url Homepage of the add-on
arch list Supported architectures
startup string ‘once’ for applications that don’t run as daemon
boot string ‘manual’ manual boot
options dict Default options value of the add-on
schema dict Schema for options value of the add-on
map list List of maps for additional folders

Full configuration options and further details can be found here

Configuring the docker container

The Dockerfile is shown below. Home Assistant will build the container using an Alpine Linux base operating system with selection of the base image matching the machine’s architecture. The dockerfile installs the required packages and then runs the bash script run.sh on container start.

Dockerfile

ARG BUILD_FROM FROM $BUILD_FROM ENV LANG C.UTF-8 RUN apk add --no-cache jq rsync sshpass COPY run.sh / RUN chmod a+x /run.sh CMD [ "/run.sh" ]

The script

The required add-on functionality can be completed using bash scripting.

The bash script firstly extracts the user supplied configuration. The building of the add-on generates a file called options.json which stores the entered user configuration entered via the web interface. Using jq (sed for json) these variables can be extracted and assigned to variables for use in the rest of the script.

CONFIG_PATH=/data/options.json

KEEP_DAYS=$(jq --raw-output ".keep_days" $CONFIG_PATH)
RSYNC_ADDRESS=$(jq --raw-output ".server_address" $CONFIG_PATH)
RSYNC_DIR=$(jq --raw-output ".destination_directory" $CONFIG_PATH)
RSYNC_USER=$(jq --raw-output ".username" $CONFIG_PATH)
RSYNC_PASSWD=$(jq --raw-output ".password" $CONFIG_PATH)

Then the script has two parts. First part is to delete files older than a specified time. This is achieved with this command:

find \ # search for files in a directory hierarchy  
/backup \ # directory to search  
-mtime +$KEEP_DAYS \ # file's data was last modified n*24 hours ago; n supplied by KEEP_DAY variable  
-type f \ # file is a regulare file type  
--delete # delete files that match above conditions

Then the second part is to transfer the files to an rsync server. The script needed to allow for transfer to both authenticated and un-authenticated servers. Whether to use authentication is determined by testing if no username was supplied I.e RSYNC_PASSWD string is empty:

[ -z "$RSYNC_USER"] = True #$RSYNC_USER is an empty string

Un-authenticated servers are able to receive files by the following rsync command:

rsync \ # a fast, versatile, remote (and local) file-copying tool  
-a \ # implies recursion and want to preserve almost everything, e.g. symbolic links, devices, attributes, permissions, ownerships  
-v \ # increases the amount of information output  
--delete \ # delete extraneous files from dest dirs  
/backup/ \ # source directory  
rsync://$RSYNC_ADDRESS/$RSYNC_DIR # remote rsync server address  

Authenticated servers require the additional:

ssh-pass \ # noninteractive ssh password provider  
-p $RSYNC_PASSWD \ # password is given on the command line; supplied by RSYNC_PASSWD variable  
rsync... # command passwd is supplied to  

The completed bash script is below:

run.sh

#!/bin/bash CONFIG_PATH=/data/options.json KEEP_DAYS=$(jq --raw-output ".keep_days" $CONFIG_PATH) RSYNC_ADDRESS=$(jq --raw-output ".server_address" $CONFIG_PATH) RSYNC_DIR=$(jq --raw-output ".destination_directory" $CONFIG_PATH) RSYNC_USER=$(jq --raw-output ".username" $CONFIG_PATH) RSYNC_PASSWD=$(jq --raw-output ".password" $CONFIG_PATH) echo "[Info] Starting remove and rsync" echo "[Info] Removing backups older than $KEEP_DAYS days" find /backup -mtime +$KEEP_DAYS -type f -delete echo "[Info] Running rsync push to $RSYNC_ADDRESS into $RSYNC_DIR" if [ -z "$RSYNC_USER" ] then echo "[Info] Pushing to rsync server without user" rsync -av --delete /backup/ rsync://$RSYNC_ADDRESS/$RSYNC_DIR else echo "[Info] Pushing to rsync server with user: $RSYNC_USER" sshpass -p $RSYNC_PASSWD rsync -av --delete /backup/ rsync://$RSYNC_USER@$RSYNC_ADDRESS/$RSYNC_DIR fi echo "[Info] Finished"

Deploying the add-on

The guide at https://www.home-assistant.io/hassio/installing_third_party_addons/ explains the process of adding a custom repository. The custom repository URL is:

https://gitlab.com/jselby/hassio-addons

Follow the README on how to configure the add-on once the repository is added.


May 2020