Samba Backup
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
#!/command/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
# shellcheck disable=SC1091
|
||||
|
||||
source scripts/config.sh
|
||||
source scripts/main.sh
|
||||
source scripts/helper.sh
|
||||
source scripts/precheck.sh
|
||||
source scripts/sensor.sh
|
||||
|
||||
export __BASHIO_LOG_TIMESTAMP="%y-%m-%d %T"
|
||||
|
||||
function run-backup {
|
||||
(
|
||||
# synchronize the backup routine
|
||||
flock -n -x 200 || { bashio::log.warning "Backup already running. Trigger ignored."; return 0; }
|
||||
|
||||
bashio::log.info "Backup running ..."
|
||||
get-sensor
|
||||
update-sensor "${SAMBA_STATUS[1]}"
|
||||
|
||||
# run entire backup steps
|
||||
# shellcheck disable=SC2015
|
||||
create-backup && copy-backup && cleanup-backups-local && cleanup-backups-remote \
|
||||
&& update-sensor "${SAMBA_STATUS[2]}" "ALL" \
|
||||
|| update-sensor "${SAMBA_STATUS[3]}" "ALL"
|
||||
|
||||
sleep 10
|
||||
update-sensor "${SAMBA_STATUS[0]}"
|
||||
bashio::log.info "Backup finished"
|
||||
) 200>/tmp/samba_backup.lockfile
|
||||
}
|
||||
|
||||
|
||||
# init config and sensor
|
||||
get-config
|
||||
get-sensor
|
||||
|
||||
# run precheck and exit entire addon in case the check fails
|
||||
if [ "$SKIP_PRECHECK" = true ]; then
|
||||
update-sensor "${SAMBA_STATUS[0]}"
|
||||
elif ! smb-precheck; then
|
||||
update-sensor "${SAMBA_STATUS[3]}"
|
||||
exit 1
|
||||
else
|
||||
update-sensor "${SAMBA_STATUS[0]}" "ALL"
|
||||
fi
|
||||
|
||||
bashio::log.info "Samba Backup started successfully"
|
||||
|
||||
# check the time in the background
|
||||
if [[ "$TRIGGER_TIME" != "manual" ]]; then
|
||||
{
|
||||
bashio::log.debug "Starting main loop ..."
|
||||
while true; do
|
||||
current_date=$(date +'%a %H:%M')
|
||||
[[ "$TRIGGER_DAYS" =~ ${current_date:0:3} && "$current_date" =~ $TRIGGER_TIME ]] && run-backup
|
||||
|
||||
sleep 60
|
||||
done
|
||||
} &
|
||||
fi
|
||||
|
||||
# start the stdin listener in foreground
|
||||
bashio::log.debug "Starting stdin listener ..."
|
||||
while true; do
|
||||
read -r input
|
||||
bashio::log.debug "Input received: ${input}"
|
||||
input=$(echo "$input" | jq -r .)
|
||||
|
||||
if [ "$input" = "restore-sensor" ]; then
|
||||
restore-sensor
|
||||
|
||||
elif [ "$input" = "reset-counter" ]; then
|
||||
get-sensor && reset-counter
|
||||
bashio::log.info "Counter variables reset successfully"
|
||||
|
||||
elif [ "$input" = "trigger" ]; then
|
||||
run-backup
|
||||
|
||||
elif is-extended-trigger "$input"; then
|
||||
bashio::log.info "Running backup with customized parameters"
|
||||
overwrite-params "$input" && run-backup && restore-params
|
||||
|
||||
else
|
||||
bashio::log.warning "Received unknown input: ${input}"
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/command/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
# user input variables
|
||||
declare TARGET_DIR
|
||||
declare KEEP_LOCAL
|
||||
declare KEEP_REMOTE
|
||||
declare TRIGGER_TIME
|
||||
declare TRIGGER_DAYS
|
||||
declare EXCLUDE_ADDONS
|
||||
declare EXCLUDE_FOLDERS
|
||||
declare BACKUP_NAME
|
||||
declare BACKUP_PWD
|
||||
declare SKIP_PRECHECK
|
||||
|
||||
|
||||
# smbclient command strings
|
||||
declare SMB
|
||||
declare ALL_SHARES
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Read and print config.
|
||||
# ------------------------------------------------------------------------------
|
||||
function get-config {
|
||||
local host
|
||||
local share
|
||||
local username
|
||||
local password
|
||||
local workgroup
|
||||
|
||||
host=$(bashio::config 'host' | escape-input)
|
||||
share=$(bashio::config 'share' | escape-input)
|
||||
username=$(bashio::config 'username' | escape-input)
|
||||
password=$(bashio::config 'password' | escape-input)
|
||||
bashio::config.exists 'workgroup' && workgroup=$(bashio::config 'workgroup' | escape-input) || workgroup=""
|
||||
|
||||
TARGET_DIR=$(bashio::config 'target_dir')
|
||||
KEEP_LOCAL=$(bashio::config 'keep_local')
|
||||
KEEP_REMOTE=$(bashio::config 'keep_remote')
|
||||
TRIGGER_TIME=$(bashio::config 'trigger_time')
|
||||
TRIGGER_DAYS=$(bashio::config 'trigger_days')
|
||||
EXCLUDE_ADDONS=$(bashio::config 'exclude_addons')
|
||||
EXCLUDE_FOLDERS=$(bashio::config 'exclude_folders')
|
||||
|
||||
bashio::config.exists 'backup_name' && BACKUP_NAME=$(bashio::config 'backup_name') || BACKUP_NAME=""
|
||||
bashio::config.exists 'backup_password' && BACKUP_PWD=$(bashio::config 'backup_password') || BACKUP_PWD=""
|
||||
bashio::config.true 'skip_precheck' && SKIP_PRECHECK=true || SKIP_PRECHECK=false
|
||||
|
||||
if [[ -n "$username" && -n "$password" ]]; then
|
||||
SMB="smbclient -U \"${username}\"%\"${password}\" \"//${host}/${share}\" 2>&1 -t 180"
|
||||
ALL_SHARES="smbclient -U \"${username}\"%\"${password}\" -L \"//${host}\" 2>&1"
|
||||
else
|
||||
SMB="smbclient -N \"//${host}/${share}\" 2>&1 -t 180"
|
||||
ALL_SHARES="smbclient -N -L \"//${host}\" 2>&1"
|
||||
fi
|
||||
|
||||
# non-default workgroup?
|
||||
[ -n "$workgroup" ] && SMB="${SMB} -W \"${workgroup}\""
|
||||
[ -n "$workgroup" ] && ALL_SHARES="${ALL_SHARES} -W \"${workgroup}\""
|
||||
|
||||
# legacy SMB protocols allowed?
|
||||
bashio::config.true 'compatibility_mode' && SMB="${SMB} --option=\"client min protocol\"=\"NT1\""
|
||||
bashio::config.true 'compatibility_mode' && ALL_SHARES="${ALL_SHARES} --option=\"client min protocol\"=\"NT1\""
|
||||
|
||||
bashio::log.info "---------------------------------------------------"
|
||||
bashio::log.info "Host/Share: ${host}/${share}"
|
||||
bashio::log.info "Target directory: ${TARGET_DIR}"
|
||||
bashio::log.info "Keep local/remote: ${KEEP_LOCAL}/${KEEP_REMOTE}"
|
||||
bashio::log.info "Trigger time: ${TRIGGER_TIME}"
|
||||
[[ "$TRIGGER_TIME" != "manual" ]] && bashio::log.info "Trigger days: $(echo "$TRIGGER_DAYS" | xargs)"
|
||||
bashio::log.info "---------------------------------------------------"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Escape input given by the user.
|
||||
#
|
||||
# Returns the escaped string on stdout
|
||||
# ------------------------------------------------------------------------------
|
||||
function escape-input {
|
||||
local input
|
||||
read -r input
|
||||
|
||||
# escape the evil dollar sign
|
||||
input=${input//$/\\$}
|
||||
|
||||
echo "$input"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Overwrite the backup parameters.
|
||||
#
|
||||
# Arguments
|
||||
# $1 The json input string
|
||||
# ------------------------------------------------------------------------------
|
||||
function overwrite-params {
|
||||
local input="$1"
|
||||
local addons
|
||||
local folders
|
||||
local name
|
||||
local password
|
||||
|
||||
addons=$(echo "$input" | jq '.exclude_addons[]' 2>/dev/null)
|
||||
[[ "$addons" != null ]] && EXCLUDE_ADDONS="$addons"
|
||||
|
||||
folders=$(echo "$input" | jq '.exclude_folders[]' 2>/dev/null)
|
||||
[[ "$folders" != null ]] && EXCLUDE_FOLDERS="$folders"
|
||||
|
||||
name=$(echo "$input" | jq -r '.backup_name')
|
||||
[[ "$name" != null ]] && BACKUP_NAME="$name"
|
||||
|
||||
password=$(echo "$input" | jq -r '.backup_password')
|
||||
[[ "$password" != null ]] && BACKUP_PWD="$password"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Restore the original backup parameters.
|
||||
# ------------------------------------------------------------------------------
|
||||
function restore-params {
|
||||
EXCLUDE_ADDONS=$(bashio::config 'exclude_addons')
|
||||
EXCLUDE_FOLDERS=$(bashio::config 'exclude_folders')
|
||||
bashio::config.exists 'backup_name' && BACKUP_NAME=$(bashio::config 'backup_name') || BACKUP_NAME=""
|
||||
bashio::config.exists 'backup_password' && BACKUP_PWD=$(bashio::config 'backup_password') || BACKUP_PWD=""
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#!/command/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Create the backup name by replacing all name patterns.
|
||||
#
|
||||
# Returns the final name on stdout
|
||||
# ------------------------------------------------------------------------------
|
||||
function generate-backup-name {
|
||||
local name
|
||||
local theversion
|
||||
local thetype
|
||||
local thedate
|
||||
|
||||
if [ -n "$BACKUP_NAME" ]; then
|
||||
# get all values
|
||||
theversion=$(ha core info --raw-json | jq -r .data.version)
|
||||
[[ -n "$EXCLUDE_ADDONS" || -n "$EXCLUDE_FOLDERS" ]] && thetype="Partial" || thetype="Full"
|
||||
thedate=$(date +'%Y-%m-%d %H:%M')
|
||||
|
||||
# replace the string patterns with the real values
|
||||
name="$BACKUP_NAME"
|
||||
name=${name/\{version\}/$theversion}
|
||||
name=${name/\{type\}/$thetype}
|
||||
name=${name/\{date\}/$thedate}
|
||||
else
|
||||
name="Samba Backup $(date +'%Y-%m-%d %H:%M')"
|
||||
fi
|
||||
|
||||
echo "$name"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Create a valid filename by replacing all forbidden characters.
|
||||
#
|
||||
# Arguments
|
||||
# $1 The original name
|
||||
#
|
||||
# Returns the final name on stdout
|
||||
# ------------------------------------------------------------------------------
|
||||
function generate-filename {
|
||||
local input="${1}"
|
||||
local prefix
|
||||
|
||||
declare -a forbidden=('\/' '\\' '\<' '\>' '\:' '\"' '\|' '\?' '\*' '\.' '\..' '\ ' '\-')
|
||||
for fc in "${forbidden[@]}"; do
|
||||
input=${input//$fc/_}
|
||||
done
|
||||
|
||||
prefix=${input:0:13}
|
||||
[ "$prefix" = "Samba_Backup_" ] && echo "${input}" || echo "Samba_Backup_${input}"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Run a command and log its output (debug or warning).
|
||||
#
|
||||
# Arguments
|
||||
# $1 The command to run
|
||||
#
|
||||
# Returns 1 in case the command failed
|
||||
# ------------------------------------------------------------------------------
|
||||
function run-and-log {
|
||||
local cmd="$1"
|
||||
local result
|
||||
|
||||
if result=$(eval "$cmd"); then
|
||||
[ -n "$result" ] && bashio::log.debug "$result"
|
||||
else
|
||||
bashio::log.warning "$result"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Checks if input is an extended trigger.
|
||||
#
|
||||
# Arguments
|
||||
# $1 The input to check
|
||||
#
|
||||
# Returns 0 (true) or 1 (false)
|
||||
# ------------------------------------------------------------------------------
|
||||
function is-extended-trigger {
|
||||
local input=${1}
|
||||
local cmd
|
||||
|
||||
if cmd=$(echo "$input" | jq -r '.command' 2>/dev/null); then
|
||||
[ "$cmd" = "trigger" ] && return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/command/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
|
||||
declare SLUG
|
||||
declare SNAP_NAME
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Create a new backup (full or partial).
|
||||
# ------------------------------------------------------------------------------
|
||||
function create-backup {
|
||||
local args
|
||||
local addons
|
||||
local folders
|
||||
|
||||
SNAP_NAME=$(generate-backup-name)
|
||||
|
||||
args=()
|
||||
args+=("--name" "$SNAP_NAME")
|
||||
[ -n "$BACKUP_PWD" ] && args+=("--password" "$BACKUP_PWD")
|
||||
|
||||
# do we need a partial backup?
|
||||
if [[ -n "$EXCLUDE_ADDONS" || -n "$EXCLUDE_FOLDERS" ]]; then
|
||||
# include all installed addons that are not listed to be excluded
|
||||
addons=$(ha addons --raw-json | jq -rc '.data.addons[] | select (.installed != false) | .slug')
|
||||
for ad in ${addons}; do [[ ! $EXCLUDE_ADDONS =~ $ad ]] && args+=("-a" "$ad"); done
|
||||
|
||||
# include all folders that are not listed to be excluded
|
||||
folders=(homeassistant ssl share addons/local media)
|
||||
for fol in "${folders[@]}"; do [[ ! $EXCLUDE_FOLDERS =~ $fol ]] && args+=("-f" "$fol"); done
|
||||
fi
|
||||
|
||||
# run the command
|
||||
bashio::log.info "Creating backup \"${SNAP_NAME}\""
|
||||
SLUG="$(ha backups new "${args[@]}" --raw-json | jq -r .data.slug)"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copy the latest backup to the remote share.
|
||||
# ------------------------------------------------------------------------------
|
||||
function copy-backup {
|
||||
local store_name
|
||||
local input
|
||||
local count
|
||||
|
||||
if [ "$SLUG" = "null" ]; then
|
||||
bashio::log.error "Error occurred! Backup could not be created! Please try again"
|
||||
return 1
|
||||
fi
|
||||
|
||||
store_name=$(generate-filename "$SNAP_NAME")
|
||||
|
||||
# append number to filename if already existing
|
||||
input="$(eval "${SMB} -c 'cd \"${TARGET_DIR}\"; ls'")"
|
||||
count=$(echo "$input" | grep "\<$store_name.*\.tar\>" | wc -l)
|
||||
(( "$count" > 0 )) && store_name="${store_name}${count}.tar" || store_name="${store_name}.tar"
|
||||
|
||||
bashio::log.info "Copying backup ${SLUG} (${store_name}) to share"
|
||||
cd /backup || return 1
|
||||
|
||||
if ! run-and-log "${SMB} -c 'cd \"${TARGET_DIR}\"; put ${SLUG}.tar ${store_name}'"; then
|
||||
bashio::log.warning "Could not copy backup ${SLUG} to share. Trying again ..."
|
||||
sleep 5
|
||||
run-and-log "${SMB} -c 'cd \"${TARGET_DIR}\"; put ${SLUG}.tar ${store_name}'"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Delete old local backups.
|
||||
# ------------------------------------------------------------------------------
|
||||
function cleanup-backups-local {
|
||||
local snaps
|
||||
local slug
|
||||
local name
|
||||
|
||||
[ "$KEEP_LOCAL" == "all" ] && return 0
|
||||
|
||||
snaps=$(ha backups --raw-json | jq -c '.data.backups[] | {date,slug,name}' | sort -r)
|
||||
bashio::log.debug "List of local backups:\n$snaps"
|
||||
|
||||
echo "$snaps" | tail -n +$((KEEP_LOCAL + 1)) | while read -r backup; do
|
||||
slug=$(echo "$backup" | jq -r .slug)
|
||||
name=$(echo "$backup" | jq -r .name)
|
||||
bashio::log.info "Deleting ${slug} (${name}) local"
|
||||
run-and-log "ha backups remove ${slug}"
|
||||
done
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Delete old backups on the share.
|
||||
# ------------------------------------------------------------------------------
|
||||
function cleanup-backups-remote {
|
||||
local input
|
||||
local snaps
|
||||
|
||||
[ "$KEEP_REMOTE" == "all" ] && return 0
|
||||
|
||||
# read all tar files that match the backup name pattern and sort them
|
||||
input="$(eval "${SMB} -c 'cd \"${TARGET_DIR}\"; ls'")"
|
||||
snaps="$(echo "$input" | grep -E '\<([0-9a-f]{8}|Samba_Backup_.*)\.tar\>' | while read -r name _ _ _ a b c d; do
|
||||
theDate=$(echo "$a $b $c $d" | xargs -i date +'%Y-%m-%d %H:%M' -d "{}")
|
||||
echo "$theDate $name"
|
||||
done | sort -r)"
|
||||
bashio::log.debug "List of remote backups:\n$snaps"
|
||||
|
||||
echo "$snaps" | tail -n +$((KEEP_REMOTE + 1)) | while read -r _ _ name; do
|
||||
bashio::log.info "Deleting ${name} on share"
|
||||
run-and-log "${SMB} -c 'cd \"${TARGET_DIR}\"; rm ${name}'"
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/command/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Perform a pre-check if the Samba share is configured correctly.
|
||||
#
|
||||
# Returns 1 in case of a failure
|
||||
# ------------------------------------------------------------------------------
|
||||
function smb-precheck {
|
||||
local result
|
||||
local shares
|
||||
|
||||
# check if we can access the share at all
|
||||
if ! result=$(eval "${SMB} -c 'exit'"); then
|
||||
bashio::log.warning "$result"
|
||||
|
||||
# host not found
|
||||
if [[ "$result" =~ "NT_STATUS_NOT_FOUND" ]]; then
|
||||
bashio::log.fatal "The provided host cannot be found. If you've specified a DNS name, please try using an IP address instead."
|
||||
|
||||
# host unreachable
|
||||
elif [[ "$result" =~ "NT_STATUS_HOST_UNREACHABLE" ]]; then
|
||||
bashio::log.fatal "The provided host is unreachable. Please check your config and network."
|
||||
|
||||
# SMB1 problem
|
||||
elif [[ "$result" =~ "NT_STATUS_CONNECTION_DISCONNECTED" ]]; then
|
||||
bashio::log.fatal "Cannot access share. It seems that your share only supports insecure SMB protocols."
|
||||
bashio::log.fatal "If you want me to connect, please check the \"compatibility_mode\" option. Use at your own risk."
|
||||
|
||||
# share does not exist
|
||||
elif [[ "$result" =~ "NT_STATUS_BAD_NETWORK_NAME" ]]; then
|
||||
bashio::log.fatal "Cannot access share. It seems that your configured share does not exist."
|
||||
|
||||
# try to find out which shares exist
|
||||
if shares=$(eval "${ALL_SHARES}"); then
|
||||
bashio::log.fatal "I found the following shares on your host. Did you mean one of those?"
|
||||
bashio::log.fatal "$shares"
|
||||
fi
|
||||
|
||||
# access denied
|
||||
elif [[ "$result" =~ "NT_STATUS_ACCESS_DENIED" ]]; then
|
||||
bashio::log.fatal "Cannot access share. Access denied. Please check your share permissions."
|
||||
|
||||
# login failed
|
||||
elif [[ "$result" =~ "NT_STATUS_LOGON_FAILURE" ]]; then
|
||||
bashio::log.fatal "Cannot access share. Login failed. Please check your credentials."
|
||||
|
||||
# unknown reason
|
||||
else
|
||||
bashio::log.fatal "Cannot access share. Unknown reason."
|
||||
fi
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check if the target directory exists
|
||||
if ! run-and-log "${SMB} -c 'cd \"${TARGET_DIR}\"'"; then
|
||||
bashio::log.fatal "Target directory does not exist. Please check your config."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check if we have write permissions
|
||||
touch samba-tmp123
|
||||
if ! run-and-log "${SMB} -c 'cd \"${TARGET_DIR}\"; put samba-tmp123; rm samba-tmp123'"; then
|
||||
bashio::log.fatal "Missing write permissions on target folder. Please check your share settings."
|
||||
return 1
|
||||
fi
|
||||
rm samba-tmp123
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
#!/command/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
|
||||
declare SAMBA_STATUS=(IDLE RUNNING SUCCEEDED FAILED)
|
||||
declare SENSOR_NAME="sensor.samba_backup"
|
||||
declare SENSOR_URL="/core/api/states/${SENSOR_NAME}"
|
||||
declare STORAGE_FILE="/backup/.samba_backup.sensor"
|
||||
|
||||
declare CURRENT_STATUS
|
||||
|
||||
declare BACKUPS_LOCAL="0"
|
||||
declare BACKUPS_REMOTE="0"
|
||||
declare TOTAL_SUCCESS="0"
|
||||
declare TOTAL_FAIL="0"
|
||||
declare LAST_BACKUP="never"
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Get the current sensor values and store them in internal variables.
|
||||
# ------------------------------------------------------------------------------
|
||||
function get-sensor {
|
||||
local storage
|
||||
local result
|
||||
|
||||
if [ -f "$STORAGE_FILE" ]; then
|
||||
storage=$(cat "$STORAGE_FILE")
|
||||
|
||||
if result=$(echo "$storage" | jq -r ".attributes.backups_local" 2>/dev/null); then
|
||||
[[ "$result" != null ]] && BACKUPS_LOCAL="$result"
|
||||
fi
|
||||
|
||||
if result=$(echo "$storage" | jq -r ".attributes.backups_remote" 2>/dev/null); then
|
||||
[[ "$result" != null ]] && BACKUPS_REMOTE="$result"
|
||||
fi
|
||||
|
||||
if result=$(echo "$storage" | jq -r ".attributes.total_backups_succeeded" 2>/dev/null); then
|
||||
[[ "$result" != null ]] && TOTAL_SUCCESS="$result"
|
||||
fi
|
||||
|
||||
if result=$(echo "$storage" | jq -r ".attributes.total_backups_failed" 2>/dev/null); then
|
||||
[[ "$result" != null ]] && TOTAL_FAIL="$result"
|
||||
fi
|
||||
|
||||
if result=$(echo "$storage" | jq -r ".attributes.last_backup" 2>/dev/null); then
|
||||
[[ "$result" != null ]] && LAST_BACKUP="$result"
|
||||
fi
|
||||
fi
|
||||
|
||||
bashio::log.debug "Backups local/remote: ${BACKUPS_LOCAL}/${BACKUPS_REMOTE}"
|
||||
bashio::log.debug "Total backups succeeded/failed: ${TOTAL_SUCCESS}/${TOTAL_FAIL}"
|
||||
bashio::log.debug "Last backup: ${LAST_BACKUP}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Update the Home Assistant sensor.
|
||||
#
|
||||
# Arguments
|
||||
# $1 The status
|
||||
# $2 Whether to update all values
|
||||
# ------------------------------------------------------------------------------
|
||||
function update-sensor {
|
||||
local status=${1}
|
||||
local all=${2:-}
|
||||
local data
|
||||
local response
|
||||
|
||||
CURRENT_STATUS="$status"
|
||||
|
||||
if bashio::var.has_value "${all}"; then
|
||||
if response=$(ha backups --raw-json | jq ".data.backups[].slug"); then
|
||||
[ -n "$response" ] && BACKUPS_LOCAL=$(echo "$response" | wc -l) || BACKUPS_LOCAL="0"
|
||||
fi
|
||||
|
||||
if response=$(eval "${SMB} -c 'cd \"${TARGET_DIR}\"; ls'"); then
|
||||
# grep returns non-zero exit code if there are no matches
|
||||
if response=$(echo "$response" | grep -E '\<([0-9a-f]{8}|Samba_Backup_.*)\.tar\>'); then
|
||||
BACKUPS_REMOTE=$(echo "$response" | wc -l)
|
||||
else
|
||||
BACKUPS_REMOTE="0"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CURRENT_STATUS" = "${SAMBA_STATUS[2]}" ]; then
|
||||
TOTAL_SUCCESS=$((TOTAL_SUCCESS + 1))
|
||||
LAST_BACKUP=$(date +'%Y-%m-%d %H:%M')
|
||||
elif [ "$CURRENT_STATUS" = "${SAMBA_STATUS[3]}" ]; then
|
||||
TOTAL_FAIL=$((TOTAL_FAIL + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
data=$(jq -n \
|
||||
--arg s "$CURRENT_STATUS" \
|
||||
--arg bl "$BACKUPS_LOCAL" \
|
||||
--arg br "$BACKUPS_REMOTE" \
|
||||
--arg ts "$TOTAL_SUCCESS" \
|
||||
--arg tf "$TOTAL_FAIL" \
|
||||
--arg lb "$LAST_BACKUP" \
|
||||
'{
|
||||
"state": $s,
|
||||
"attributes": {
|
||||
"friendly_name": "Samba Backup",
|
||||
"backups_local": $bl,
|
||||
"backups_remote": $br,
|
||||
"total_backups_succeeded": $ts,
|
||||
"total_backups_failed": $tf,
|
||||
"last_backup": $lb
|
||||
}
|
||||
}')
|
||||
|
||||
if ! response=$(ha-post-sensor "$data"); then
|
||||
bashio::log.error "Unable to update sensor ${SENSOR_NAME} in Home Assistant"
|
||||
fi
|
||||
|
||||
echo "$data" > "$STORAGE_FILE"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Restore the Home Assistant sensor with the last known values.
|
||||
# ------------------------------------------------------------------------------
|
||||
function restore-sensor {
|
||||
local data
|
||||
local response
|
||||
|
||||
if [ -f "$STORAGE_FILE" ]; then
|
||||
data=$(cat "$STORAGE_FILE")
|
||||
|
||||
if ! response=$(ha-post-sensor "$data"); then
|
||||
bashio::log.error "Unable to restore sensor ${SENSOR_NAME} in Home Assistant"
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Reset the counter variables of the Home Assistant sensor.
|
||||
# ------------------------------------------------------------------------------
|
||||
function reset-counter {
|
||||
TOTAL_SUCCESS="0"
|
||||
TOTAL_FAIL="0"
|
||||
|
||||
update-sensor "$CURRENT_STATUS"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ----------------------- INTERNAL FUNCTION ------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
# Post a sensor via the REST API of Home Assistant.
|
||||
#
|
||||
# Arguments:
|
||||
# $1 The JSON data to POST
|
||||
# ------------------------------------------------------------------------------
|
||||
function ha-post-sensor {
|
||||
local data=${1}
|
||||
local status
|
||||
local response
|
||||
|
||||
bashio::log.debug "Posting sensor data to API at ${SENSOR_URL}"
|
||||
|
||||
if ! response=$(curl --silent --show-error \
|
||||
--write-out '\n%{http_code}' --request "POST" \
|
||||
-H "Authorization: Bearer ${__BASHIO_SUPERVISOR_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "${data}" \
|
||||
"${__BASHIO_SUPERVISOR_API}${SENSOR_URL}"
|
||||
); then
|
||||
bashio::log.debug "${response}"
|
||||
bashio::log.error "Something went wrong contacting the API"
|
||||
return 1
|
||||
fi
|
||||
|
||||
status=${response##*$'\n'}
|
||||
response=${response%$status}
|
||||
|
||||
bashio::log.debug "API Status: ${status}"
|
||||
bashio::log.debug "API Response: ${response}"
|
||||
|
||||
if [[ "${status}" -eq 401 ]]; then
|
||||
bashio::log.error "Unable to authenticate with the API, permission denied"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "${response}"
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user