#!/usr/bin/env bash set -e declare -a MISSING_PACKAGES function info { echo -e "\e[32m[info] $*\e[39m"; } function warn { echo -e "\e[33m[warn] $*\e[39m"; } function error { echo -e "\e[31m[error] $*\e[39m"; exit 1; } BATCH_MODE=0 # Parse command line parameters while [[ $# -gt 0 ]]; do arg="$1" case $arg in -m|--machine) MACHINE=$2 shift ;; -d|--data-share) DATA_SHARE=$2 shift ;; -p|--prefix) PREFIX=$2 shift ;; -s|--sysconfdir) SYSCONFDIR=$2 shift ;; -b|--batch) BATCH_MODE=1 ;; *) error "Unrecognized option $1" ;; esac shift done PREFIX=${PREFIX:-/usr} SYSCONFDIR=${SYSCONFDIR:-/etc} DATA_SHARE=${DATA_SHARE:-$PREFIX/share/hassio} CONFIG=$SYSCONFDIR/hassio.json warn "" warn "If you want more control over your own system, run" warn "Home Assistant as a VM or run Home Assistant Core" warn "via a Docker container." warn "" if [ $BATCH_MODE -eq 0 ]; then warn "If you want to abort, hit ctrl+c within 10 seconds..." warn "" sleep 10 else warn "Batch mode enabled, continuing..." fi ARCH=$(uname -m) IP_ADDRESS=$(hostname -I | awk '{ print $1 }') BINARY_DOCKER=/usr/bin/docker DOCKER_REPO=homeassistant SERVICE_DOCKER="docker.service" SERVICE_NM="NetworkManager.service" FILE_DOCKER_CONF="/etc/docker/daemon.json" FILE_INTERFACES="/etc/network/interfaces" FILE_NM_CONF="/etc/NetworkManager/NetworkManager.conf" FILE_NM_CONNECTION="/etc/NetworkManager/system-connections/default" URL_RAW_BASE="https://raw.githubusercontent.com/home-assistant/supervised-installer/master/files" URL_VERSION="https://version.home-assistant.io/stable.json" URL_BIN_APPARMOR="${URL_RAW_BASE}/hassio-apparmor" URL_BIN_HASSIO="${URL_RAW_BASE}/hassio-supervisor" URL_DOCKER_DAEMON="${URL_RAW_BASE}/docker_daemon.json" URL_HA="${URL_RAW_BASE}/ha" URL_INTERFACES="${URL_RAW_BASE}/interfaces" URL_NM_CONF="${URL_RAW_BASE}/NetworkManager.conf" URL_NM_CONNECTION="${URL_RAW_BASE}/system-connection-default" URL_SERVICE_APPARMOR="${URL_RAW_BASE}/hassio-apparmor.service" URL_SERVICE_HASSIO="${URL_RAW_BASE}/hassio-supervisor.service" URL_APPARMOR_PROFILE="https://version.home-assistant.io/apparmor.txt" # Check env command -v systemctl > /dev/null 2>&1 || MISSING_PACKAGES+=("systemd") command -v nmcli > /dev/null 2>&1 || MISSING_PACKAGES+=("network-manager") command -v apparmor_parser > /dev/null 2>&1 || MISSING_PACKAGES+=("apparmor") command -v docker > /dev/null 2>&1 || MISSING_PACKAGES+=("docker") command -v jq > /dev/null 2>&1 || MISSING_PACKAGES+=("jq") command -v curl > /dev/null 2>&1 || MISSING_PACKAGES+=("curl") command -v dbus-daemon > /dev/null 2>&1 || MISSING_PACKAGES+=("dbus") if [ ! -z "${MISSING_PACKAGES}" ]; then warn "The following is missing on the host and needs " warn "to be installed and configured before running this script again" error "missing: ${MISSING_PACKAGES[@]}" fi # Check if Modem Manager is enabled if systemctl is-enabled ModemManager.service &> /dev/null; then warn "ModemManager service is enabled. This might cause issue when using serial devices." fi # Detect wrong docker logger config if [ ! -f "$FILE_DOCKER_CONF" ]; then # Write default configuration info "Creating default docker daemon configuration $FILE_DOCKER_CONF" curl -sL ${URL_DOCKER_DAEMON} > "${FILE_DOCKER_CONF}" # Restart Docker service info "Restarting docker service" systemctl restart "$SERVICE_DOCKER" else STORAGE_DRIVER=$(docker info -f "{{json .}}" | jq -r -e .Driver) LOGGING_DRIVER=$(docker info -f "{{json .}}" | jq -r -e .LoggingDriver) if [[ "$STORAGE_DRIVER" != "overlay2" ]]; then warn "Docker is using $STORAGE_DRIVER and not 'overlay2' as the storage driver, this is not supported." fi if [[ "$LOGGING_DRIVER" != "journald" ]]; then warn "Docker is using $LOGGING_DRIVER and not 'journald' as the logging driver, this is not supported." fi fi # Check dmesg access if [[ "$(sysctl --values kernel.dmesg_restrict)" != "0" ]]; then info "Fix kernel dmesg restriction" echo 0 > /proc/sys/kernel/dmesg_restrict echo "kernel.dmesg_restrict=0" >> /etc/sysctl.conf fi # Create config for NetworkManager info "Creating NetworkManager configuration" curl -sL "${URL_NM_CONF}" > "${FILE_NM_CONF}" if [ ! -f "$FILE_NM_CONNECTION" ]; then curl -sL "${URL_NM_CONNECTION}" > "${FILE_NM_CONNECTION}" fi warn "Changes are needed to the /etc/network/interfaces file" if [ $BATCH_MODE -eq 0 ]; then info "If you have modified the network on the host manualy, those can now be overwritten" info "If you do not overwrite this now you need to manually adjust it later" info "Do you want to proceed with overwriting the /etc/network/interfaces file? [N/y] " read answer < /dev/tty if [[ "$answer" =~ "y" ]] || [[ "$answer" =~ "Y" ]]; then info "Replacing /etc/network/interfaces" curl -sL "${URL_INTERFACES}" > "${FILE_INTERFACES}"; fi else info "Batch mode enabled, replacing /etc/network/interfaces" curl -sL "${URL_INTERFACES}" > "${FILE_INTERFACES}"; fi info "Restarting NetworkManager" systemctl restart "${SERVICE_NM}" # Generate hardware options case $ARCH in "i386" | "i686") MACHINE=${MACHINE:=qemux86} HASSIO_DOCKER="$DOCKER_REPO/i386-hassio-supervisor" ;; "x86_64") MACHINE=${MACHINE:=qemux86-64} HASSIO_DOCKER="$DOCKER_REPO/amd64-hassio-supervisor" ;; "arm" |"armv6l") if [ -z $MACHINE ]; then error "Please set machine for $ARCH" fi HASSIO_DOCKER="$DOCKER_REPO/armhf-hassio-supervisor" ;; "armv7l") if [ -z $MACHINE ]; then error "Please set machine for $ARCH" fi HASSIO_DOCKER="$DOCKER_REPO/armv7-hassio-supervisor" ;; "aarch64") if [ -z $MACHINE ]; then error "Please set machine for $ARCH" fi HASSIO_DOCKER="$DOCKER_REPO/aarch64-hassio-supervisor" ;; *) error "$ARCH unknown!" ;; esac if [[ ! "${MACHINE}" =~ ^(intel-nuc|odroid-c2|odroid-n2|odroid-xu|qemuarm|qemuarm-64|qemux86|qemux86-64|raspberrypi|raspberrypi2|raspberrypi3|raspberrypi4|raspberrypi3-64|raspberrypi4-64|tinker)$ ]]; then error "Unknown machine type ${MACHINE}!" fi ### Main # Init folders if [ ! -d "$DATA_SHARE" ]; then mkdir -p "$DATA_SHARE" fi # Read infos from web HASSIO_VERSION=$(curl -s $URL_VERSION | jq -e -r '.supervisor') ## # Write configuration cat > "$CONFIG" <<- EOF { "supervisor": "${HASSIO_DOCKER}", "machine": "${MACHINE}", "data": "${DATA_SHARE}" } EOF ## # Pull supervisor image info "Install supervisor Docker container" docker pull "$HASSIO_DOCKER:$HASSIO_VERSION" > /dev/null docker tag "$HASSIO_DOCKER:$HASSIO_VERSION" "$HASSIO_DOCKER:latest" > /dev/null ## # Install Hass.io Supervisor info "Install supervisor startup scripts" curl -sL ${URL_BIN_HASSIO} > "${PREFIX}/sbin/hassio-supervisor" curl -sL ${URL_SERVICE_HASSIO} > "${SYSCONFDIR}/systemd/system/hassio-supervisor.service" sed -i "s,%%HASSIO_CONFIG%%,${CONFIG},g" "${PREFIX}"/sbin/hassio-supervisor sed -i -e "s,%%BINARY_DOCKER%%,${BINARY_DOCKER},g" \ -e "s,%%SERVICE_DOCKER%%,${SERVICE_DOCKER},g" \ -e "s,%%BINARY_HASSIO%%,${PREFIX}/sbin/hassio-supervisor,g" \ "${SYSCONFDIR}/systemd/system/hassio-supervisor.service" chmod a+x "${PREFIX}/sbin/hassio-supervisor" systemctl enable hassio-supervisor.service > /dev/null 2>&1; # # Install Hass.io AppArmor info "Install AppArmor scripts" mkdir -p "${DATA_SHARE}/apparmor" curl -sL ${URL_BIN_APPARMOR} > "${PREFIX}/sbin/hassio-apparmor" curl -sL ${URL_SERVICE_APPARMOR} > "${SYSCONFDIR}/systemd/system/hassio-apparmor.service" curl -sL ${URL_APPARMOR_PROFILE} > "${DATA_SHARE}/apparmor/hassio-supervisor" sed -i "s,%%HASSIO_CONFIG%%,${CONFIG},g" "${PREFIX}/sbin/hassio-apparmor" sed -i -e "s,%%SERVICE_DOCKER%%,${SERVICE_DOCKER},g" \ -e "s,%%HASSIO_APPARMOR_BINARY%%,${PREFIX}/sbin/hassio-apparmor,g" \ "${SYSCONFDIR}/systemd/system/hassio-apparmor.service" chmod a+x "${PREFIX}/sbin/hassio-apparmor" systemctl enable hassio-apparmor.service > /dev/null 2>&1; systemctl start hassio-apparmor.service ## # Init system info "Start Home Assistant Supervised" systemctl start hassio-supervisor.service ## # Setup CLI info "Installing the 'ha' cli" curl -sL ${URL_HA} > "${PREFIX}/bin/ha" chmod a+x "${PREFIX}/bin/ha" info info "Home Assistant supervised is now installed" info "First setup will take some time, when it's ready you can reach it here:" info "http://${IP_ADDRESS}:8123" info