Дружим FreeBSD 14.0 и HomeAssistant

—

от автора

Привет, Хабр!

Многие из вас наверное слышали о Home Assistant (HA) — система домашней автоматизации с открытым исходным кодом, которая прекрасно работает на различных аппаратных решениях и поддерживает операционные систем Linux, macOS, Windows. К сожалению, в списке поддерживаемых операционных систем нет FreeBSD. А как быть тем, кто уже имеет рабочий сервер для домашней автоматизации на FreeBSD и не хочет заморачиваться с установкой дополнительного оборудования для запуска Home Assistant? Тут два варианта решения проблемы: первое решение это использование виртуальной машины с поддерживаемой операционной системой для HA, что занимает некоторые ресурсы сервера и второй вариант это установка HA непосредственно на FreeBSD. Как вы понимаете, я пошел вторым путем (путь граблей и приключений) и об этом расскажу далее.

Что ты имеем и что мы хотим?

В качестве сервера домашней автоматизации мы имеем систему с установленной операционной системой FreeBSD 14 (последняя версия на момент публикации статьи)

root@cyberex:~ # freebsd-version 14.0-RELEASE-p4

На данную систему нам необходимо установить последнюю версию HA (2024.1.3).

Давайте приступим

Для начала нам нужно установить порты, которые необходимы для установки и работы HA:

Перед установкой портов проверим обновление репозиториев с помощью команд

root@cyberex:~ # pkg update root@cyberex:~ # pkg upgrade

И инсталлируем необходимые пакеты, так как HA завершает поддержку Python 3.10, то мы установим Python 3.11:

root@cyberex:~ # pkg install rust py39-pillow py310-sqlite3 python311 openssl ffmpeg 

Затем, нам необходимо создать пользователя, под которым будет работать сервис HA:

root@cyberex:~ # pw useradd homeassistant -w no -m -c "Home Assistant"

Созданный пользователь homeassistant будет иметь домашний каталог в директории «home» с отключенным парольным доступом.

Чтобы HA мог работать с usb стиками ZigBee пропишем пользователя homeassistant в группу dialer:

root@cyberex:~ # pw groupmod dialer -m homeassistant

И подправим права на домашнюю директорию:

root@cyberex:~ # chmod 770 /home/homeassistant

Теперь нам необходимо создать директорию для установки сервера HA:

root@cyberex:~ # mkdir -p /srv/homeassistant

И сделать пользователя homeassistant ее «владельцем»:

root@cyberex:~ # chown homeassistant:homeassistant /srv/homeassistant

Далее нам нужно создать виртуальное окружение Python для изоляции пакетов HA:

заходим под пользователем homeassistant

root@cyberex:~ # su -l homeassistant

Предварительно создав символьные ссылки на Python 3.11:

root@cyberex:~ # ln -s /usr/local/bin/python3.11 /usr/local/bin/python root@cyberex:~ # ln -s /usr/local/bin/python3.11 /usr/local/bin/python3

Создаем окружение

[homeassistant@cyberex ~]$ python -m venv /srv/homeassistant

Проверяем наличие созданных папок для виртуального окружения

[homeassistant@cyberex ~]$ ls -l /srv/homeassistant total 24 drwxr-xr-x  3 homeassistant homeassistant 2048 Jan 10 18:22 bin drwxr-xr-x  2 homeassistant homeassistant  512 Jan 10 18:22 cache drwxr-xr-x  4 homeassistant homeassistant  512 Jan 10 18:22 include drwxr-xr-x  4 homeassistant homeassistant  512 Jan 10 18:22 lib lrwxr-xr-x  1 homeassistant homeassistant    3 Jan 10 18:22 lib64 -> lib -rw-r--r--  1 homeassistant homeassistant  174 Jan 10 18:22 pyvenv.cfg drwxr-xr-x  3 homeassistant homeassistant  512 Jan 10 18:22 share

Активируем виртуальное окружение, предварительно установив необходимые права на файл активации

[homeassistant@cyberex ~]$ chmod 700 /srv/homeassistant/bin/activate [homeassistant@cyberex ~]$ /srv/homeassistant/bin/activate

Затем нам нужно установить необходимые зависимости

[homeassistant@cyberex ~]$ /srv/homeassistant/bin/pip install wheel sqlalchemy fnvhash

Обновляем менеджер пакетов, если это необходимо

[homeassistant@cyberex ~]$ /srv/homeassistant/bin/python -m pip install --upgrade pip

Теперь мы можем перейти к самому важному, для чего мы здесь все собрались: установка Home Assistant. Установка выполняется следующей командой, обратите внимание, что мы до сих пор работаем под пользователем homeassistant :

[homeassistant@cyberex ~]$ /srv/homeassistant/bin/pip install homeassistant

Ждем завершение установки…. После завершения установки всех пакетов HA, мы можем выполнить первый запуск с помощью команды

[homeassistant@cyberex ~]$ /srv/homeassistant/bin/hass --ignore-os-check -v -v -v -v

Ключ —ignore-os-check необходим для запуска на «несовместимой» ОС FreeBSD, иначе наш HA просто не запустится. Ключ -v необходим отображения лога при отладке.

Теперь мы открываем адрес http://ваш_ip:8123 и наслаждается чудесным интерфейсом HA надписью 404 Not Found. Вот и приехали, подумал я.

Исправляем неисправности

  • Ошибка 404 Not Found

После того, как была получена ошибка 404 Not Found, я заглянул в консоль и увидел в логе сообщение

CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error, you have likely made a mistake with your OpenSSL configuration.

которое явно даёт понять, что возникла проблема с конфигурацией OpenSSL, а именно, отсутствующей поддержки старых методов шифрования. Чтобы исправить эту проблему, нам нужно включить поддержку, наиболее простым решением оказалось добавление в скрипт запуска HA пару строк:

Открываем скрипт запуска в текстовом редакторе

[homeassistant@cyberex ~]$ ee /srv/homeassistant/bin/hass

и добавляем следующие строки в код, которые проверяют поддержку старых методов шифрования и при отсутствии поддержки включают её:

if os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY") is None:     os.environ["CRYPTOGRAPHY_OPENSSL_NO_LEGACY"] = "1"

В итоге файл должен выглядеть следующим образом:

!/srv/homeassistant/bin/python # -*- coding: utf-8 -*- import re import sys import os from homeassistant.__main__ import main if __name__ == '__main__':     if os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY") is None:         os.environ["CRYPTOGRAPHY_OPENSSL_NO_LEGACY"] = "1"     sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])     sys.exit(main())

Сохраняем, запускаем скрипт снова и теперь ошибки 404 Not Found больше нет, сейчас мы можем воспользоваться веб интерфейсом HA для начальной настройки. Но на этом проблемы не закончились.

  • Ошибка установки NumPy 1.26.0

В процессе работы, из логов было обнаружено, что HA не может установить Python пакет NumPy версии 1.26.0, как показала тестовая «ручная» установка, проблема возникает только с этой версией, прошлые версии пакета успешно устанавливаются. Но к сожалению, HA отказывается работать со старыми версиями и пытается установить более новую версию пакета. Немного погуглив в Яндексе, было найдено следующее решение:

Установка NumPy из репозитория GitHub. Устанавливаем порт Git

root@cyberex:~ # pkg install git

Заходим под пользователем homeassistant

root@cyberex:~ # su -l homeassistant

И клонируем NumPy из Git репозитория

[homeassistant@cyberex ~]$ git clone https://github.com/numpy/numpy.git numpy-git

Устанавливаем пакет, используя последовательность команд

[homeassistant@cyberex ~]$ cd numpy-git [homeassistant@cyberex ~]$ git checkout v1.26.0 [homeassistant@cyberex ~]$ git cherry-pick 040ed2d [homeassistant@cyberex ~]$ git submodule update --init [homeassistant@cyberex ~]$ cd .. [homeassistant@cyberex ~]$ /srv/homeassistant/bin/pip install numpy-git/

После проделанных операций, NumPy 1.26.0 успешно установился и проблема была решена.

  • Ошибка установки Webrtc Noise Gain

Аналогичная проблема возникла с установкой пакета Webrtc Noise Gain, при выполнении команды установки

[homeassistant@cyberex ~]$ /srv/homeassistant/bin/pip install webrtc-noise-gain

пакет честно заявил что «Unsupported system» и отказался устанавливаться, хотя в исходном коде пакета на GitHub обнаружена поддержка FreeBSD. Для решения проблемы, будем устанавливать пакет из GitHub.

[homeassistant@cyberex ~]$ git clone http://github.com/rhasspy/webrtc-noise-gain.git [homeassistant@cyberex ~]$ cd webrtc-noise-gain [homeassistant@cyberex ~]$ python -m build --wheel [homeassistant@cyberex ~]$ cd .. [homeassistant@cyberex ~]$ srv/homeassistant/bin/pip install webrtc-noise-gain/dist/webrtc_noise_gain-1.2.3-cp311-cp311-freebsd_13_1_release_p6_amd64.whl

После установки пакета, проблема была решена.

Добавляем HomeAssistant в автозагрузку

После теста работы HA, нам необходимо добавить его сервис в автозапуск при запуске системы, это реализуется путем добавления скрипта в директорию

/usr/local/etc/rc.d/

Создаем скрипт запуска, команды выполняются под пользователем с root правами

root@cyberex:~ # ee /usr/local/etc/rc.d/homeassistant

В текстовом редакторе, вставляем следующий код

Код скрипта запуска
#!/bin/sh # ------------------------------------------------------- # Copy this file to '/usr/local/etc/rc.d/homeassistant' # `chmod +x /usr/local/etc/rc.d/homeassistant` # `sysrc homeassistant_enable=yes` # `service homeassistant start` # ------------------------------------------------------- #https://github.com/tprelog  name=homeassistant rcvar=${name}_enable  . /etc/rc.subr && load_rc_config ${name}  : "${homeassistant_enable:="NO"}" : "${homeassistant_rc_debug:="NO"}" : "${homeassistant_user:="homeassistant"}" : "${homeassistant_python:="NOT_SET"}" : "${homeassistant_venv:="/srv/homeassistant"}" : "${homeassistant_safe_mode:="NO"}" : "${homeassistant_debug:="NO"}" : "${homeassistant_skip_pip:="NO"}" : "${homeassistant_verbose:="NO"}" : "${homeassistant_color_log:="YES"}" : "${homeassistant_restart_delay:=1}"  if [ ! "$(id ${homeassistant_user} 2>/dev/null)" ]; then   err 1 "user not found: ${homeassistant_user}" else   : "${homeassistant_group:="$(id -gn ${homeassistant_user})"}"   HOME="$(getent passwd "${homeassistant_user}" | cut -d: -f6)" fi  if [ -z "${HOME}" ] || [ ! -d "${HOME}" ] || [ "${HOME}" == "/nonexistent" ] || [ "${HOME}" == "/var/empty" ]; then   : "${homeassistant_config_dir:="/home/${name}"}"   : "${homeassistant_user_dir:="${homeassistant_venv}"}"   export HOME="${homeassistant_user_dir}" else   : "${homeassistant_user_dir:="${HOME}"}"   : "${homeassistant_config_dir:="${homeassistant_user_dir}/.${name}"}" fi  [ -n "${homeassistant_cpath:-}" ] && export CPATH="${homeassistant_cpath}" [ -n "${homeassistant_library_path:-}" ] && export LIBRARY_PATH="${homeassistant_library_path}" [ -n "${homeassistant_path:-}" ] && export PATH="${homeassistant_path}"  umask "${homeassistant_umask:-022}"  logfile="/var/log/${name}_daemon.log" pidfile="/var/run/${name}_daemon.pid" pidfile_child="/var/run/${name}.pid"  command="/usr/sbin/daemon" extra_commands="check_config ensure_config upgrade install reinstall logs script test"  homeassistant_precmd() {   local _srv_ _own_ _msg_   local _venv_="${homeassistant_venv}"   local _user_="${homeassistant_user}"   if [ ! -d "${_venv_}" ]; then     _msg_="${_venv_} not found"   elif [ ! -f "${_venv_}/bin/activate" ]; then     _msg_="${_venv_}/bin/activate is not found"   elif [ ! -x "${_srv_:="${_venv_}/bin/hass"}" ]; then     _msg_="${_srv_} is not found or is not executable"   elif [ "${_own_:="$(stat -f '%Su' ${_srv_})"}" != ${_user_} ]; then     warn "${_srv_} is not owned by ${_user_}"     _msg_="${_srv_} is currently owned by ${_own_}"   else     HA_CMD="${_srv_}"     cd "${_venv_}" || err 1 "cd ${_venv_}"     return 0   fi   err 1 "${_msg_}" }   start_precmd=${name}_prestart homeassistant_prestart() {    homeassistant_precmd \   && install -g "${homeassistant_group}" -m 664 -o ${homeassistant_user} -- /dev/null "${logfile}" \   && install -g "${homeassistant_group}" -m 664 -o ${homeassistant_user} -- /dev/null "${pidfile}" \   && install -g "${homeassistant_group}" -m 664 -o ${homeassistant_user} -- /dev/null "${pidfile_child}" \   || return 1    homeassistant_ensure_config "${homeassistant_config_dir}"   HA_ARGS="--ignore-os-check --config ${homeassistant_config_dir}"    if [ -n "${homeassistant_log_file:-}" ]; then     install -g "${homeassistant_group}" -m 664 -o ${homeassistant_user} -- /dev/null "${homeassistant_log_file}" \     && HA_ARGS="${HA_ARGS} --log-file ${homeassistant_log_file}"   fi    if [ -n "${homeassistant_log_rotate_days:-}" ]; then     HA_ARGS="${HA_ARGS} --log-rotate-days ${homeassistant_log_rotate_days}"   fi    checkyesno homeassistant_color_log || HA_ARGS="${HA_ARGS} --log-no-color"   checkyesno homeassistant_debug && HA_ARGS="${HA_ARGS} --debug"   checkyesno homeassistant_safe_mode && HA_ARGS="${HA_ARGS} --safe_mode"   checkyesno homeassistant_skip_pip && HA_ARGS="${HA_ARGS} --skip_pip"   checkyesno homeassistant_verbose && HA_ARGS="${HA_ARGS} --verbose"    rc_flags="-f -o ${logfile} -P ${pidfile} -p ${pidfile_child} -R ${homeassistant_restart_delay} ${HA_CMD} ${HA_ARGS}" }  start_postcmd=${name}_poststart homeassistant_poststart() {   sleep 1 ; run_rc_command status }  restart_precmd="${name}_prerestart" homeassistant_prerestart() {   homeassistant_check_config "${homeassistant_config_dir}" }  stop_precmd=${name}_prestop homeassistant_prestop() {   local _owner_   # shellcheck disable=SC2154   if [ -n "${rc_pid}" ] && [ "${_owner_:="$(stat -f '%Su' ${pidfile_child})"}" != ${homeassistant_user} ]; then     err 1 "${homeassistant_user} can not stop a process owned by ${_owner_}"   fi }  stop_postcmd=${name}_poststop homeassistant_poststop() {   rm -f -- "${pidfile_child}"   rm -f -- "${pidfile}" }  status_cmd=${name}_status homeassistant_status() {   local _http_ _ip_   if [ -n "${rc_pid}" ]; then     : "${homeassistant_secure:="NO"}" # This is only a cosmetic variable - used by the status_cmd     _ip_="$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p')"     checkyesno homeassistant_secure && _http_="https" || _http_="http"     echo "${name} is running as pid ${rc_pid}."     echo "${_http_}://${_ip_}:${homeassistant_port:-"8123"}"  # This is only a cosmetic variable   else     echo "${name} is not running."     return 1   fi }  check_config_cmd="${name}_check_config ${1} ${2}" homeassistant_check_config() {   [ "${1}" == "check_config" ] || [ "${1}" == "onecheck_config" ] && shift   homeassistant_script check_config --config "${1:-"${homeassistant_config_dir}"}" }  ensure_config_cmd="${name}_ensure_config ${1} ${2}" homeassistant_ensure_config() {   [ "${1}" == "ensure_config" ] || [ "${1}" == "oneensure_config" ] && shift   local _config_dir_="${1:-"${homeassistant_config_dir}"}"   debug "config_dir: ${_config_dir_}"   if [ ! -d "${_config_dir_}" ]; then     install -d -g "${homeassistant_group}" -m 775 -o ${homeassistant_user} -- "${_config_dir_}" \     || err 1 "unable to create directory: ${_config_dir_}"   fi   homeassistant_script ensure_config --config "${_config_dir_}" }  script_cmd="${name}_script ${*}" homeassistant_script() {   [ "${1}" == "script" ] || [ "${1}" == "onescript" ] && shift   local _action_="${1}" ; shift   local _args_="${*}"   homeassistant_precmd   # shellcheck disable=SC2016   su - ${homeassistant_user} -c '     source ${1}/bin/activate || exit 1     hass --script ${2} ${3}     deactivate   ' _ ${homeassistant_venv} "${_action_}" "${_args_}" }  logs_cmd="${name}_logs ${*}" homeassistant_logs() {   case "${2}" in     -f )       tail -F "${logfile}" ;;     -h )       head -n "${3:-"100"}" "${logfile}" ;;     -n | -t )       tail -n "${3:-"100"}" "${logfile}" ;;     -l )       less -R "${logfile}" ;;     * )       cat "${logfile}" ;;   esac }  upgrade_cmd="${name}_upgrade" homeassistant_upgrade() {   homeassistant_precmd   run_rc_command stop 2>/dev/null ; local _rcstop_=${?}   homeassistant_install --upgrade "${name}"   homeassistant_check_config && [ ${_rcstop_} == 0 ] && run_rc_command start }  install_cmd="${name}_install ${*}" homeassistant_install() {   [ "${1}" == "install" ] || [ "${1}" == "oneinstall" ] && shift   local _create_ _arg_   _arg_="${*:-"${name}"}"   debug "install: ${_arg_}"   if [ "${1}" == "${name}" ] && { [ ! -d "${homeassistant_venv}" ] || [ ! "$(ls -A ${homeassistant_venv})" ]; }; then     debug "creating virtualenv: ${homeassistant_venv}"     install -d -g "${homeassistant_group}" -m 775 -o ${homeassistant_user} -- ${homeassistant_venv} \     || err 1 "failed to create directory: ${homeassistant_venv}"     _create_="YES"   elif [ -d "${homeassistant_venv}" ]; then     debug "found existing directory: ${homeassistant_venv}"     homeassistant_precmd   else     echo "failed to install: ${_arg_}"     err 1 "${name} is not installed: ${homeassistant_venv}"   fi   # shellcheck disable=SC2016   su - ${homeassistant_user} -c '     if [ ${1} == "YES" ]; then       ${2} -m venv ${3}       source ${3}/bin/activate || exit 1       shift 3       pip install wheel       pip install ${@}     else       source ${3}/bin/activate || exit 1       shift 3       pip install ${@}     fi     deactivate   ' _ ${_create_:-"NO"} ${homeassistant_python} ${homeassistant_venv} "${_arg_}" || err 1 "install function failed" }  reinstall_cmd="${name}_reinstall ${*}" homeassistant_reinstall() {   [ "${1}" == "reinstall" ] || [ "${1}" == "onereinstall" ] && shift   local _ans1_ _ans2_ _rcstop_ _version_ _arg_   homeassistant_precmd   if [ "${1%==*}" == "${name}" ]; then     _arg_="${*}"   elif [ -z "${_arg_}" ]; then     if [ -n "${_version_:=$(cat ${homeassistant_config_dir}/.HA_VERSION 2>/dev/null)}" ]; then       _arg_="${name}==${_version_}"     else       _arg_="${name}"     fi   else     warn "expecting ${name} to be listed first"     err 1 "check args: ${*}"   fi   echo -e "\n${orn}You are about to recreate the virtualenv:${end}\n  ${homeassistant_venv}\n"   echo -e "${orn}The following package(s) will be installed:${end}\n  ${_arg_}\n"   read -rp " Type 'YES' to continue: " _ans1_   run_rc_command stop 2>/dev/null ; _rcstop_=${?}   cd / ; rm -r -- "${homeassistant_venv}" || err 1 "failed to remove ${homeassistant_venv}"   { homeassistant_install ${_arg_} ; homeassistant_check_config ; } \   && [ ${_rcstop_} == 0 ] && run_rc_command start }  test_cmd="${name}_test" homeassistant_test() {   echo -e "\nTesting virtualenv...\n"   homeassistant_precmd   ## Switch users / activate virtualenv / run a command   # shellcheck disable=SC2016   su "${homeassistant_user}" -c '     echo -e "  $(pwd)\n"     source ${1}/bin/activate     echo "  $(python --version)"     echo "  Home Assistant $(pip show homeassistant | grep Version | cut -d" " -f2)"     deactivate   ' _ ${homeassistant_venv}   echo }  colors () {   export red=$'\e[1;31m'   export orn=$'\e[38;5;208m'   export end=$'\e[0m' } ; colors  checkyesno homeassistant_rc_debug && rc_debug="ON" run_rc_command "${1}"

Чтобы сделать наш скрипт исполняемым, назначим необходимые права

root@cyberex:~ # chmod 755 /usr/local/etc/rc.d/homeassistant

Добавляем скрипт в автозагрузку

root@cyberex:~ # sysrc homeassistant_enable="YES"

Запускаем наш сервис Home Assistant

root@cyberex:~ # service homeassistant start

Итоги

В данной статье я описал свой опыт установки Home Assistant на сервер с операционной системой FreeBSD. Статья задумывалась как несложная инструкция, чтобы сохранить последовательность действий при установке Home Assistant. Надеюсь она поможет кому-то сэкономить свое время.

Спасибо за внимание! Всем добра!


ссылка на оригинал статьи https://habr.com/ru/articles/787700/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *