Home Assistant

Home Assistant For Webmasters: Managed Dropbox Backups

Did you know that Home Assistant can be far more than just a home assistant? It is probably not too far fetched to say that it can manage, schedule and automate all technological aspects of your life.

Like one third of the other webmasters in the world, I run this site on WordPress. I was looking for a solution to manage backups and of course I turned to Home Assistant.

If you have a website, WordPress or otherwise, you can manage and automate many of the backend processes with the awesome Home Assistant and this article will show you how!

Bitmoji Image

Prerequisite

This tutorial is more advanced and requires some understanding of Home Assistant plus a good understanding of Linux and the basic terminal commands. You will also need the SSH & Web Terminal add-on installed.

If you are not yet confident writing configurations in YAML then I would recommend that you take a look at my other tutorials on automation and scripts first.

In order to link your website backend to Home Assistant, you will need be able to access your site via SSH using the shell integration. I would recommend checking out my tutorial on Home Assistant shell integration if you have not yet used shell commands in your configuration.

At the time of writing this site is hosted on Siteground, which offers SSH access on all of their plans. If you are not using Siteground then you will need to check if SSH access is available with your plan and chosen web host.

As there are different configurations for Home Assistant and some complications accessing external storage media with some setups, I opted to use Dropbox as a universal solution compatible with all setups. You will therefore need a Dropbox account, although the bash script could be modified to save your backups elsewhere.

Accessing your site with terminal

The method for setting up SSH access to your website will differ depending on your hosting company. You will need to follow the guide for setting up SSH specific to your hosting provider.

To demonstrate how it is done, I will document the process for my chosen hosting company, Siteground. Your process should be similar if using a different hosting company.

If you have problems with the following steps, I would highly recommend checking out my tutorial on SSH with the shell integration to gain a better understanding.

Getting a SSH key

The first thing we need to is look at the instructions for accessing SSH provided by the web hosting company. Siteground require us to create a new private key and copy it to our local machine. A new key can be created with the key manager tool found in the members area.

We can create a new key specifically for Home Assistant to gain access to the website backend. We will name this key “Home Assistant” to differentiate it from SSH access from the main system terminal. Once the key is created we can go ahead and copy it to the clipboard.

Next we need to create a new file in the Home Assistant /config directory called website_key.txt with a text editor. It is important that this file is saved in plain text format. Copy and paste the key into the text file and then save it.

/config/website_key.txt

Adding the key to Home Assistant

Now that we have the key saved in a file inside of the Home Assistant config directory, we can add it to Home Assistant. Go ahead and open the Home Assistant terminal. You can either use the web terminal or you can SSH to it with a terminal program.

First we need to give the key file the correct permissions.

chmod 600 /config/website_key.txt

In order to add the key we must first ensure that the SSH agent is running by entering the following command.

eval "$(ssh-agent -s)"

The command should echo the PID of the SSH agent. Now we can go ahead and add the key to Home Assistant.

ssh-add /config/website_key.txt

Once the key has been added, we need to copy it back to the /config/ssh directory so that Home Assistant can access is separately from the terminal. If you do not have the ssh directory, you can just create it.

cp ~/.ssh/id_rsa ~/config/ssh

Now you should have access to your website backend via SSH from both the Home Assistant terminal and from within the YAML configuration. Your SSH credentials will be provided by your hosting company, you can use them to log in to your website backend bash shell.

<username>@<yoursite.com> -p <port>

Getting a Dropbox token

We will be writing a bash script that will run on our web server backend, which copies the SQL database and pushes it, along with all of the other website files, to a Dropbox account. Home Assistant will call the bash script and pass arguments based upon settings made in the UI.

In order to be able to send files from the bash script, we will need to use the Dropbox API. This will require us to obtain an OAuth token from the developer site. Just complete the following steps to grab your OAuth token.

  1. Head over to the Dropbox developers website and log in.
  2. Choose the option from Dropbox API on the first step.
  3. Choose full dropbox access on the next step.
  4. Give you app a unique name.
  5. Click create app.
  6. Scroll down to the OAuth 2 section and click generate.
  7. Copy the token to a plain text file for using later.

Writing a Bash script

Now that we have access to our website backend through SSH and the dropbox key, we can create a script to manage the backups. All of the files can be found in the Github repository for this project.

Arguments

In order to give Home Assistant control of the bash script, we will pass arguments to the script from the shell_command integration and load them into variables. For clarity the arguments are as follows.

ArgumentVariableDescription
$1overwriteBoolean for overwrite option
$2usernameMySQL database username
$3databaseMySQL database name
$4passwordMySQL database password
$5dropbox_keyDropbox OAuth token
$6website_pathRoot of website files
$7dropbox_pathPath to store backup on Dropbox
$8log_pathPath to public location of last backup timestamp file

The overwriting option allows you to choose between creating date stamped iterations of the database each time the backup is performed, or just to overwrite a single file. The latter being beneficial for those using a free Dropbox account with limited storage space.

Your MySQL credentials and database name will be available through your hosting management area. I would advise that you create a new user with read-only privileges to use with this script for additional security.

The website path should be specified as the root of your website. All files and folders from this point will be backed up.

~/www/yoursite.com

The Dropbox path is the location in Dropbox that you wish to store your backup. All files specified from the root of the website will be copied to this path.

/website_backup

The log path is a publicly accessible website address which hosts a simple text file containing the datetime stamp from the last backup. Home Assistant can use the curl command to fetch this data in order be displayed in the user interface. You can see the last backup of this site at https://siytek.com/dropbox_backup.log and the log path is as follows.

~/www/siytek.com/public_html/dropbox_backup.log

The first part of the script takes the arguments passed and places them inside variables with more meaningful names.

#!/bin/bash

# Script config
thedate="-"$(date +"%d_%m_%Y")
datetime=$(date '+%d/%m/%Y %H:%M:%S');
website_path=$6 # Path to website root folder
overwrite=$1
log_path=$8

# SQL credentials
username="$2"
database="$3"
password="$4"

# Dropbox config
dropbox_key="$5"
dropbox_path=$7 # Backup storage location

MySQL defaults file

The next part of the script creates a temporary file in which to place the MySQL password. This is necessary in order to avoid having to manually type the password, which of course is not possible when calling the script remotely.

# Create defaults file
echo "[mysqld]" > sql_pw
echo "[mysqldump]" >> sql_pw
echo "password=""'"${password}"'" >> sql_pw

Copy the database with MySQLdump

The script will then make a local backup copy the database using MySQLdump. The IF statement determines whether or not a date stamped version of the file should be created.

# If database overwrite set to FALSE then add dated backup
if [ $overwrite = "off" ]; then
        echo " adding new backup database-backup${thedate}.sql"
        mysqldump --defaults-file=./sql_pw --single-transaction -u ${username} ${database} > ./database-backup${thedate}.sql
# Else just overwrite the same database backup each time

else
    	echo " overwriting database-backup.sql"
        mysqldump --defaults-file=./sql_pw --single-transaction -u ${username} ${database} > ./database-backup.sql
        thedate=""
fi

Remote the MySQL defaults file

Once the database has been copied to a local backup, the MySQL defaults files is removed. As mentioned before it is advisable to create a MySQL user with read-only privileges for this purpose for additional security.

# Removing defaults file
rm sql_pw

Transfer the backup to Dropbox

Now we can go ahead and transfer the database backup to Dropbox using the API.

echo "Transfering database to Dropbox..."
curl -X POST "https://content.dropboxapi.com/2/files/upload" \
    --header "Authorization: Bearer ${dropbox_key}" \
    --header "Dropbox-API-Arg: {\"path\": \"${dropbox_path}/database-backup${thedate}.sql\",\"mode\": \"overwrite\",\"autorename\": true,\"mute\": false}" \
    --header "Content-Type: application/octet-stream" \
    --data-binary @database-backup${thedate}.sql \
    > ./dropbox_database_backup.log

Once the database has been uploaded we can transfer all of the additional websites files using an iterative loop for each file in all of the subfolders for the specified website root folder.

rm dropbox_website_backup.log
for file in $(find ${website_path} -name '*' -type f); do

       curl -X POST "https://content.dropboxapi.com/2/files/upload" \
       --header "Authorization: Bearer ${dropbox_key}" \
       --header "Dropbox-API-Arg: {\"path\": \"${dropbox_path}${file#${website_path}}\",\"mode\": \"overwrite\",\"autorename\": true,\"mute\": false}" \
       --header "Content-Type: application/octet-stream" \
       --data-binary @$file \
       >> ./dropbox_website_backup.log
done

In both cases a log file is written but errors are not yet handled. A more sophisticated version of this script should handle errors and report back to Home Assistant to ensure backup reliability. For now the log files must be periodically checked manually.

Writing the datetime stamp

Finally we can write a new datetime stamp in the publically accessible log so that Home Assistant can report the last time that the backup was run in the UI.

# Writing to the log file
echo "Writing date and time to ${website_path}/dropbox_backup.log"
echo ${datetime} > $log_path

echo "Done!"

The completed script

The completed script should be uploaded to your web server backend so that it can be called remotely by Home Assistant.

I would recommend putting it in the home directory for simplicity. You can download the script from the Github repository.

An easy way to put the script on your web server is just to copy the raw script and paste it into a new file created in the terminal.

nano ha_backup.sh
# Home Assistant Website Backup Manager script V0.1
# Pushes a backup copy of the website and SQL database to Dropbox
# Triggered and controled by Home Assistant Frontend
# For more information visit https://siytek.com
# The awesome Home Assistant: https://home-assistant.io

#!/bin/bash

# Script config
thedate="-"$(date +"%d_%m_%Y")
datetime=$(date '+%d/%m/%Y %H:%M:%S');
website_path=$6 # Path to website root folder
overwrite=$1
log_path=$8

# SQL credentials
username="$2"
database="$3"
password="$4"

# Dropbox config
dropbox_key="$5"
dropbox_path=$7 # Backup storage location

# Back up the database locally
echo -n "Backing up database..."

# Create defaults file
echo "[mysqld]" > sql_pw
echo "[mysqldump]" >> sql_pw
echo "password=""'"${password}"'" >> sql_pw

# If database overwrite set to FALSE then add dated backup
if [ $overwrite = "off" ]; then
        echo " adding new backup database-backup${thedate}.sql"
        mysqldump --defaults-file=./sql_pw --single-transaction -u ${username} ${database} > ./database-backup${thedate}.sql
# Else just overwrite the same database backup each time

else
    	echo " overwriting database-backup.sql"
        mysqldump --defaults-file=./sql_pw --single-transaction -u ${username} ${database} > ./database-backup.sql
        thedate=""
fi

# Removing defaults file
rm sql_pw

# Backup database to Dropbox
echo "Transfering database to Dropbox..."
curl -X POST "https://content.dropboxapi.com/2/files/upload" \
    --header "Authorization: Bearer ${dropbox_key}" \
    --header "Dropbox-API-Arg: {\"path\": \"${dropbox_path}/database-backup${thedate}.sql\",\"mode\": \"overwrite\",\"autorename\": true,\"mute\": false}" \
    --header "Content-Type: application/octet-stream" \
    --data-binary @database-backup${thedate}.sql \
    > ./dropbox_database_backup.log


echo "Backing up archive to Dropbox..."

rm dropbox_website_backup.log
for file in $(find ${website_path} -name '*' -type f); do

       curl -X POST "https://content.dropboxapi.com/2/files/upload" \
       --header "Authorization: Bearer ${dropbox_key}" \
       --header "Dropbox-API-Arg: {\"path\": \"${dropbox_path}${file#${website_path}}\",\"mode\": \"overwrite\",\"autorename\": true,\"mute\": false}" \
       --header "Content-Type: application/octet-stream" \
       --data-binary @$file \
       >> ./dropbox_website_backup.log
done

# Writing to the log file
echo "Writing date and time to ${website_path}/dropbox_backup.log"
echo ${datetime} > $log_path

echo "Done!"

Configure Home Assistant

Now that the web server backend has been configured we can build the Home Assistant user interface. Each integration is listed in a separate file for simplicity, however you can combine them all into configuration.yaml if you prefer.

You can view all of the YAML files on the Github repository.

Input boolean integration

First we will add a switch to control whether or not we wish to create date stamped backups or whether we wish to conserve space and overwrite one backup file.

We will also add a switch that allows us to turn on automated backups that occur at a specified time each day.

wordpress_overwrite:
  name: "Write Over Backup"
  icon: mdi:harddisk-remove
# Option to backup automatically at specified time
wordpress_backup_enabled:
  name: "Auto Backup On/Off"
    icon: mdi:harddisk

Shell command integration

Next we will create a shell command that launches the remote script bash on the web server and passes all of the variables. You will need to change the arguments and server details accordingly.

The first argument is set using a template that extracts the state of the overwrite switch and passes it to the bash script.

wordpress_backup: ssh -i /config/ssh/id_rsa -o 'StrictHostKeyChecking=no' <user>@<domain> -p <port> '<path to script> {{states("input_boolean.wordpress_overwrite")}} <MySQL Username> <MySQL Database> <MySQL Password> <Dropbox Key> <Public HTML path> <Dropbox path> <Timedate path>'

Script integration

In order for us to have a button in the user interface that can manually activate the backup, we will create a script that files the shell command. Adding this script to the user interface will give us a button to launch the command.

backup_wordpress:
  alias: "Backup WordPress"
  sequence:
    service: shell_command.wordpress_backup

Sensor integration

Next we will create a sensor that uses a curl command to fetch the timedate stamp from the last time the script was run. You will need to enter the public HTML path that matches the associated argument when calling the script.

We will also create a sensor.time instance so that we can compare the time with a user specified value in order to trigger the backup automatically at a desired time.

- platform: command_line
  name: "Wordpress Last Backup"
  command: curl https://<your-domain>/<your-log-file>.log
# Initilizes the date and time sensors
- platform: time_date
  display_options:
    - 'time'
    - 'date'

Input datetime integration

We also need to create an input that allows the user to input a time in which the backup should run automatically. Here we can specify the time by a text box input.

wordpress_backup_time:
  name: "Auto Backup Time"
  has_time: true
  has_date: false

Automation integration

Finally we need to create an automation that triggers the shell command if the user specified time matches the actual time and if automatic backups are enabled.

alias: "Fire wordpress backup"
trigger:
  platform: template
  value_template: "{{ states('sensor.time') == (states.input_datetime.wordpress_backup_time.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
condition:
  condition: state
  entity_id: input_boolean.wordpress_backup_enabled
  state: 'on'
action:
  service: script.turn_on
  entity_id: script.backup_wordpress

Adding the UI

With all of the entities created we can now finally add them to the user interface to create a control panel for our managed website backup.

In order to do this we need to add a new entities card with a desired title. We can turn off the header toggle as it is not required.

This will produce a simple yet effective card allowing us to manage remote website backups. Awesome!

Conclusion

Home Assistant is far more than just simple home automation software and this article gives an insight into just how powerful it can be. The shell command offers huge potential of linking not just external devices but external services.

I believe that Home Assistant has the potential to outgrow its own name and in the future it could automate all aspects of our lives, including our businesses and external services.

Congratulations if you managed to implement this idea! Hopefully it at least taught you some of the true potential of Home Assistant. Why not grab a nice beverage and get stuck in to some more of my awesome Home Assistant tutorials!

Bitmoji Image

Leave a Reply

Your email address will not be published. Required fields are marked *

back to top