├── .editorconfig ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── hassctl └── hassctl.conf /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.md text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dale3h/hassctl/97c8d58a59891f0dcb98de99634d92d396016251/.gitignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dale Higgs <@dale3h> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hassctl Flattr this 2 | 3 | A command line utility to simplify the management of Home Assistant 4 | 5 | ## Compatibility 6 | 7 | This utility has been tested on the following platforms: 8 | 9 | * Raspberry Pi 10 | * Manual install (with `systemd` service) 11 | * AIO/One-Step Installer 12 | * HASSbian Image 13 | * Ubuntu Server 16.04.1 14 | * Manual install (with `systemd` service) 15 | 16 | ## Installation 17 | 18 | `sudo curl -o /usr/local/bin/hassctl https://raw.githubusercontent.com/dale3h/hassctl/master/hassctl && sudo chmod +x /usr/local/bin/hassctl` 19 | 20 | ## Updating Home Assistant 21 | 22 | The safest way to update Home Assistant in a production environment is to run: 23 | 24 | `hassctl update-hass && hassctl config && hassctl restart` 25 | 26 | This set of commands will update Home Assistant, run a configuration check, and then restart. 27 | 28 | If the update fails, the configuration check will not run. 29 | 30 | If the configuration check fails, Home Assistant will not be restarted. 31 | 32 | If you would like to install a specific version of Home Assistant, run: 33 | 34 | `hassctl update-hass 0.43.1` (replace `0.43.1` with the version you wish to install) 35 | 36 | ## Updating `hassctl` 37 | 38 | To update `hassctl` to the latest stable version, run `hassctl update-hassctl` or `hassctl update-hassctl master` 39 | 40 | To update `hassctl` to a custom branch on this repo, run `hassctl update-hassctl branch-name` 41 | 42 | ## Usage 43 | 44 | ### You can update Home Assistant using: 45 | 46 | **`$ hassctl update-hass`** - Update Home Assistant to the latest version on PyPi 47 | 48 | **`$ hassctl update-hass 0.47.0`** - Update Home Assistant to version 0.47.0 49 | 50 | ### You can control Home Assistant using: 51 | 52 | **`$ hassctl start`** - Start the Home Assistant service 53 | 54 | **`$ hassctl stop`** - Stop the Home Assistant service 55 | 56 | **`$ hassctl restart`** - Restart the Home Assistance service 57 | 58 | **`$ hassctl kill`** - Send a SIGKILL (-9) signal to the Home Assistant service 59 | 60 | **`$ hassctl log`** - Follow the Home Assistant logs (errors are highlighted) 61 | 62 | **`$ hassctl error`** - Follow the Home Assistant error logs 63 | 64 | **`$ hassctl debug`** - Follow the Home Assistant debug logs 65 | 66 | **`$ hassctl zwave`** - Follow the Open Z-Wave logs 67 | 68 | **`$ hassctl config`** - Run the configuration check script 69 | 70 | **`$ hassctl update-hassctl [branch]`** - Update `hassctl` to the latest version 71 | 72 | ## Configuration Examples 73 | 74 | The configuration file is located at `/etc/hassctl.conf`. 75 | 76 | ### HASSbian 77 | 78 | ``` 79 | HASSCTL_BRANCH=master 80 | 81 | VIRTUAL_ENV=/srv/homeassistant 82 | PIP_EXEC=$VIRTUAL_ENV/bin/pip3 83 | HASS_EXEC=$VIRTUAL_ENV/bin/hass 84 | 85 | HASS_CONFIG=/home/homeassistant/.homeassistant 86 | HASS_USER=homeassistant 87 | HASS_SERVICE=home-assistant@homeassistant.service 88 | 89 | OZW_LOG=$HASS_CONFIG/OZW_Log.txt 90 | ``` 91 | 92 | ### Current AIO Installer 93 | 94 | ``` 95 | HASSCTL_BRANCH=master 96 | 97 | VIRTUAL_ENV=/srv/homeassistant/homeassistant_venv 98 | PIP_EXEC=$VIRTUAL_ENV/bin/pip3 99 | HASS_EXEC=$VIRTUAL_ENV/bin/hass 100 | 101 | HASS_CONFIG=/home/homeassistant/.homeassistant 102 | HASS_USER=homeassistant 103 | HASS_SERVICE=home-assistant.service 104 | 105 | OZW_LOG=$HASS_CONFIG/OZW_Log.txt 106 | ``` 107 | 108 | ### Pre-December 2016 AIO Installer 109 | 110 | ``` 111 | HASSCTL_BRANCH=master 112 | 113 | VIRTUAL_ENV=/srv/hass/hass_venv 114 | PIP_EXEC=$VIRTUAL_ENV/bin/pip3 115 | HASS_EXEC=$VIRTUAL_ENV/bin/hass 116 | 117 | HASS_CONFIG=/home/hass/.homeassistant 118 | HASS_USER=hass 119 | HASS_SERVICE=home-assistant.service 120 | 121 | OZW_LOG=$HASS_CONFIG/OZW_Log.txt 122 | ``` 123 | -------------------------------------------------------------------------------- /hassctl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | HASSCTL_VERSION="1.2.1" 4 | 5 | debug() { 6 | (( $DEBUG )) && echo "$(basename $0): DEBUG:" "$@" 7 | } 8 | 9 | info() { 10 | echo "$(basename $0):" "$@" 11 | } 12 | 13 | warn() { 14 | echo "$(basename $0):" "$@" >&2 15 | } 16 | 17 | die() { 18 | local rc=1 19 | local message= 20 | 21 | [[ $# -gt 1 ]] && rc=$1 && shift 22 | [[ ! -z "$@" ]] && warn "$@" 23 | 24 | exit $rc 25 | } 26 | 27 | usage() { 28 | echo "usage: $(basename $0) {help|version|start|stop|restart|kill|log|error|debug|zwave|config|update-hassctl|update-hass|backup|service}" 2>&1 29 | exit 1 30 | } 31 | 32 | version() { 33 | echo "$HASSCTL_VERSION" 34 | exit 0 35 | } 36 | 37 | # Show usage if no arguments given 38 | if [[ $# -lt 1 ]]; then 39 | usage 40 | fi 41 | 42 | # Report unimplemented command 43 | no_command() { 44 | warn "command not yet implemented: $1" 45 | usage 46 | } 47 | 48 | # Absolute paths to executables 49 | chmod=/bin/chmod 50 | cp=/bin/cp 51 | rm=/bin/rm 52 | curl=/usr/bin/curl 53 | systemctl=/bin/systemctl 54 | journalctl=/bin/journalctl 55 | tail=/usr/bin/tail 56 | sed=/bin/sed 57 | 58 | # Get the current username 59 | me="$(whoami)" 60 | 61 | # Portable version of realpath 62 | realpath() { 63 | [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" 64 | } 65 | 66 | # HASSCTL_CONF can be overridden as an env var 67 | HASSCTL_CONF="${HASSCTL_CONF:-/etc/hassctl.conf}" 68 | HASSCTL_CONF="$(realpath "$HASSCTL_CONF")" 69 | 70 | # Read configuration file 71 | read_config() { 72 | # Set the repository branch (can be overridden in config) 73 | HASSCTL_BRANCH="${HASSCTL_BRANCH:-master}" 74 | 75 | # Try to download config if it doesn't exist 76 | [[ -f "$HASSCTL_CONF" ]] || download_config 77 | 78 | # Load config if it exists 79 | if [[ -f "$HASSCTL_CONF" ]]; then 80 | source "$HASSCTL_CONF" 81 | else 82 | warn "$HASSCTL_CONF: config file missing, using defaults" 83 | fi 84 | 85 | # Get this scripts full path 86 | HASSCTL_EXEC="$(realpath $0)" 87 | 88 | # Confirm virtualenv path exists or is empty 89 | VIRTUAL_ENV="${VIRTUAL_ENV-/srv/homeassistant}" 90 | [[ -z "$VIRTUAL_ENV" ]] && warn "continuing without VIRTUAL_ENV" 91 | [[ -z "$VIRTUAL_ENV" ]] || [[ -d "$VIRTUAL_ENV" ]] \ 92 | || die "$VIRTUAL_ENV: no such directory -- please update VIRTUAL_ENV in $HASSCTL_CONF" 93 | 94 | # Confirm Home Assistant config path exists 95 | HASS_CONFIG="${HASS_CONFIG:-/home/homeassistant/.homeassistant}" 96 | [[ -z "$HASS_CONFIG" ]] || [[ -d "$HASS_CONFIG" ]] \ 97 | || die "$HASS_CONFIG: no such directory -- please update HASS_CONFIG in $HASSCTL_CONF" 98 | 99 | # Verify user exists 100 | HASS_USER="${HASS_USER:-homeassistant}" 101 | id -u "$HASS_USER" >/dev/null || die "$HASS_USER: no such user -- please update HASS_USER in $HASSCTL_CONF" 102 | 103 | HASSCTL_BRANCH="${HASSCTL_BRANCH:-master}" 104 | HASS_SERVICE="${HASS_SERVICE:-home*assistant*}" 105 | OZW_LOG="${OZW_LOG:-$HASS_CONFIG/OZW_Log.txt}" 106 | 107 | if [[ ! -z "$VIRTUAL_ENV" ]]; then 108 | PIP_EXEC="${PIP_EXEC:-$VIRTUAL_ENV/bin/pip3}" 109 | HASS_EXEC="${HASS_EXEC:-$VIRTUAL_ENV/bin/hass}" 110 | else 111 | PIP_EXEC="${PIP_EXEC:-$(which pip3)}" 112 | HASS_EXEC="${HASS_EXEC:-$(which hass)}" 113 | fi 114 | 115 | [[ -f "$PIP_EXEC" ]] || die "$PIP_EXEC: no such file -- please update PIP_EXEC in $HASSCTL_CONF" 116 | [[ -x "$PIP_EXEC" ]] || die "$PIP_EXEC: file not executable -- please update PIP_EXEC in $HASSCTL_CONF" 117 | 118 | [[ -f "$HASS_EXEC" ]] || die "$HASS_EXEC: no such file -- please update HASS_EXEC in $HASSCTL_CONF" 119 | [[ -x "$HASS_EXEC" ]] || die "$HASS_EXEC: file not executable -- please update HASS_EXEC in $HASSCTL_CONF" 120 | 121 | return 0 122 | } 123 | 124 | download_config() { 125 | [[ ! -f "$HASSCTL_CONF" ]] || return 126 | 127 | BRANCH="${1:-$HASSCTL_BRANCH}" 128 | BRANCH="${BRANCH:-master}" 129 | 130 | CONFIG_URL="https://raw.githubusercontent.com/dale3h/hassctl/$BRANCH/hassctl.conf" 131 | 132 | debug "downloading default config from $CONFIG_URL" 133 | 134 | HTTP_CODE="$(sudo $curl --write-out %{http_code} --silent --show-error --output "$HASSCTL_CONF" "$CONFIG_URL")" 135 | 136 | if [[ $? -ne 0 ]] || [[ "$HTTP_CODE" != "200" ]]; then 137 | # Clean up failed config file 138 | [[ -f "$HASSCTL_CONF" ]] && sudo $rm "$HASSCTL_CONF" 139 | debug "could not install default config" 140 | else 141 | debug "installed default config to $HASSCTL_CONF" 142 | fi 143 | } 144 | 145 | # Execute commands for a specific user 146 | maybe_exec_as() { 147 | local RUN_AS="$1" 148 | shift 149 | 150 | if [[ "$RUN_AS" != "$me" ]] && id -u "$RUN_AS" >/dev/null; then 151 | debug "sudo -u \"$RUN_AS\" -H \"$@\"" 152 | sudo -u "$RUN_AS" -H "$@" 153 | else 154 | debug "\"$@\"" 155 | "$@" 156 | fi 157 | } 158 | 159 | # Update hassctl script 160 | update_hassctl() { 161 | read_config 162 | 163 | BRANCH="${1:-$HASSCTL_BRANCH}" 164 | BRANCH="${BRANCH:-master}" 165 | 166 | BRANCH_URL="https://raw.githubusercontent.com/dale3h/hassctl/$BRANCH" 167 | HASSCTL_URL="$BRANCH_URL/hassctl" 168 | CONFIG_URL="$BRANCH_URL/hassctl.conf" 169 | 170 | debug "installing from $HASSCTL_URL" 171 | 172 | CP_OPTS="--remove-destination" 173 | 174 | sudo $curl -s -S -o "$HASSCTL_EXEC.update" "$HASSCTL_URL" \ 175 | && sudo $chmod +x "$HASSCTL_EXEC.update" \ 176 | && sudo $cp $CP_OPTS "$HASSCTL_EXEC.update" "$HASSCTL_EXEC" \ 177 | && sudo $rm "$HASSCTL_EXEC.update" 178 | 179 | if [[ $? -ne 0 ]]; then 180 | # Clean up if something went wrong 181 | sudo $rm "$HASSCTL_EXEC.update" 182 | die "utility update failed" 183 | fi 184 | 185 | info "utility has been updated to the latest $BRANCH version" 186 | exit 0 187 | } 188 | 189 | # Update homeassistant package 190 | update_hass() { 191 | read_config 192 | local VERSION="$1" 193 | 194 | if [[ ! -z $VERSION ]]; then 195 | VERSION="==$VERSION" 196 | fi 197 | 198 | maybe_exec_as "$HASS_USER" "$PIP_EXEC" install --upgrade homeassistant$VERSION 199 | exit $? 200 | } 201 | 202 | # Run hass check_config script 203 | check_config() { 204 | read_config 205 | maybe_exec_as "$HASS_USER" "$HASS_EXEC" --script check_config -c "$HASS_CONFIG" "$@" 206 | } 207 | 208 | # Show Home Assistant raw log 209 | hass_raw_log() { 210 | read_config 211 | debug "clear && sudo $journalctl \"$@\" --no-pager -o cat -fu \"$HASS_SERVICE\"" 212 | clear && sudo $journalctl "$@" --no-pager -o cat -fu "$HASS_SERVICE" 213 | } 214 | 215 | # Show Home Assistant error log 216 | hass_error_log() { 217 | read_config 218 | debug "clear && sudo $journalctl \"$@\" --no-pager -o cat -fu \"$HASS_SERVICE\" | grep -iP '^ .*$|^(?!.*is_failed=)(?!.*service=remove_failed_node)(?!.*service=replace_failed_node).*(error|errno|warning|exception|failure|failed|warn|except|fail|traceback).*'" 219 | clear && sudo $journalctl "$@" --no-pager -o cat -fu "$HASS_SERVICE" | grep -iP '^ .*$|^(?!.*is_failed=)(?!.*service=remove_failed_node)(?!.*service=replace_failed_node).*(error|errno|warning|exception|failure|failed|warn|except|fail|traceback).*' 220 | } 221 | 222 | # Show Home Assistant debug log 223 | hass_debug_log() { 224 | read_config 225 | debug "clear && sudo $journalctl \"$@\" --no-pager -o cat -fu \"$HASS_SERVICE\" | grep -iP '[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} DEBUG'" 226 | clear && sudo $journalctl "$@" --no-pager -o cat -fu "$HASS_SERVICE" | grep -iP '[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} DEBUG' 227 | } 228 | 229 | # Kill Home Assistant service 230 | hass_kill() { 231 | read_config 232 | debug "sudo $systemctl kill --signal=SIGKILL \"$HASS_SERVICE\"" 233 | sudo $systemctl kill --signal=SIGKILL "$HASS_SERVICE" 234 | } 235 | 236 | # Kill Home Assistant using alternative method 237 | hass_kill_alt() { 238 | debug "ps aux | grep python3 | grep hass | grep -vE grep | awk '{print $2}' | xargs kill -9" 239 | ps aux | grep python3 | grep hass | grep -vE grep | awk '{print $2}' | xargs kill -9 240 | } 241 | 242 | # Pass commands to Home Assistant service 243 | systemctl_cmd() { 244 | read_config 245 | debug "sudo $systemctl \"$@\" \"$HASS_SERVICE\"" 246 | sudo $systemctl "$@" "$HASS_SERVICE" 247 | } 248 | 249 | # Show Z-Wave log 250 | zwave_log() { 251 | read_config 252 | 253 | [[ -f "$OZW_LOG" ]] || die "$OZW_LOG: no such file -- please update OZW_LOG in $HASSCTL_CONF" 254 | [[ -r "$OZW_LOG" ]] || die "$OZW_LOG: file not readable -- please update OZW_LOG in $HASSCTL_CONF" 255 | 256 | debug "clear && $tail \"$@\" -f \"$OZW_LOG\"" 257 | clear && $tail "$@" -f "$OZW_LOG" | $sed \ 258 | -e 's/\(.*Always,.*\)/\o033[35m\1\o033[39m/' \ 259 | -e 's/\(.*Detail,.*\)/\o033[36m\1\o033[39m/' \ 260 | -e 's/\(.*Info,.*\)/\o033[32m\1\o033[39m/' \ 261 | -e 's/\(.*Warning,.*\)/\o033[33m\1\o033[39m/' \ 262 | -e 's/\(.*Error,.*\)/\o033[31m\1\o033[39m/' 263 | } 264 | 265 | # Process arguments 266 | case "$1" in 267 | help) 268 | usage 269 | ;; 270 | 271 | log) 272 | hass_raw_log "${@:2}" 273 | ;; 274 | 275 | error) 276 | hass_error_log "${@:2}" 277 | ;; 278 | 279 | debug) 280 | hass_debug_log "${@:2}" 281 | ;; 282 | 283 | zwave) 284 | zwave_log "${@:2}" 285 | ;; 286 | 287 | config) 288 | check_config "${@:2}" 289 | ;; 290 | 291 | update-hassctl) 292 | update_hassctl "${@:2}" 293 | ;; 294 | 295 | update-hass) 296 | update_hass "${@:2}" 297 | ;; 298 | 299 | backup) 300 | no_command "$@" 301 | ;; 302 | 303 | service) 304 | no_command "$@" 305 | ;; 306 | 307 | kill) 308 | hass_kill 309 | ;; 310 | 311 | kill-alt) 312 | hass_kill_alt 313 | ;; 314 | 315 | list-units|list-sockets|list-timers|start|stop|\ 316 | reload|restart|reload-or-restart|is-active|is-enabled|\ 317 | is-failed|status|show|cat|enable|disable) 318 | systemctl_cmd "$@" 319 | ;; 320 | 321 | version) 322 | version 323 | ;; 324 | 325 | *) 326 | usage 327 | ;; 328 | esac 329 | -------------------------------------------------------------------------------- /hassctl.conf: -------------------------------------------------------------------------------- 1 | # /etc/hassctl.conf: `hassctl' configuration. 2 | # 3 | # A full description of the configuration file is at 4 | # https://github.com/dale3h/hassctl 5 | 6 | HASSCTL_BRANCH=master 7 | 8 | VIRTUAL_ENV=/srv/homeassistant 9 | PIP_EXEC=$VIRTUAL_ENV/bin/pip3 10 | HASS_EXEC=$VIRTUAL_ENV/bin/hass 11 | 12 | HASS_CONFIG=/home/homeassistant/.homeassistant 13 | HASS_USER=homeassistant 14 | HASS_SERVICE="home*assistant*" 15 | 16 | OZW_LOG=$HASS_CONFIG/OZW_Log.txt 17 | --------------------------------------------------------------------------------