Просмотрено
Category: Linux

Интеграция ИБП CyberPower в Home Assistant

Интеграция ИБП CyberPower в Home Assistant

В данной статье я расскажу как добавить мониторинг ИБП в Home Assistant на примере CyberPower Value500EI.

У данной модели есть USB разъем для мониторинга состояния ИБП. Для мониторинга у CyberPower есть своя утилита под названием PowerPanel. Скачать ее можно с сайта производителя. У меня на домашнем сервере стоит Ubuntu 20.04, по этому я скачал PowerPanel for Linux 64bit (deb).

Подключаем ИБП через USB и устанавливаем PowerPanel:

 dpkg -i CyberPower_PPL_Linux+64bit+\(deb\)_v1.4.1.deb

После установки проверяем командой:

pwrstat -status

В ответ, если все нормально, должны увидеть примерно следующее:

    The UPS information shows as following:

    Properties:
            Model Name................... Value500EI
            Firmware Number.............. BZAB100.B31
            Rating Voltage............... 230 V
            Rating Power................. 275 Watt(500 VA)

    Current UPS status:
            State........................ Normal
            Power Supply by.............. Utility Power
            Utility Voltage.............. 224 V
            Output Voltage............... 226 V
            Battery Capacity............. 100 %
            Remaining Runtime............ 31 min.
            Load......................... 49 Watt(18 %)
            Line Interaction............. None
            Test Result.................. Unknown
            Last Power Event............. Blackout at 2022/09/13 23:53:27 for 7 sec.

В файле /etc/pwrstatd.conf настраиваем выключение компьютера через время после перехода на резервное питание или при разряде аккумулятора на определенный процент. Также можно настроить отправку E-Mail или выполнение произвольного скрипта. После изменения перезапускаем демона:

service pwrstatd restart

У меня настроено выключение сервера при разряде аккумулятора до 35%. Мой конфиг:

# pwrstatd configuration file
#

# You must restart pwrstatd after changing this file in order for changes to take effect.
# Ex:/etc/init.d/pwrstatd restart

#
# Action setting for event of Power Failure
#

# A delay time in seconds since event of Power Failure occur then to run shell
# script and shutdown system. Allowed range is 0 ~ 3600. Default is 60 sec.
powerfail-delay = 60

# Enable to run shell script when the event of Power Failure occur.
# The allowed options are yes and no. Default is yes.
powerfail-active = yes

# Assign a path of script file for event of Power Failure.
# The default is /etc/pwrstatd-powerfail.sh
powerfail-cmd-path = /etc/pwrstatd-powerfail.sh

# How much time in seconds to take script running for event of Power Failure.
# The allowed range is 0 ~ 3600. Default is 0 sec.
powerfail-duration = 0

# Allow Daemon to shutdown system for event of Power Failure.
# The allowed options are yes and no. Default is yes.
powerfail-shutdown = no

#
# Action setting for event of Battery Low
#

# A threshold of Battery Capacity, If the battery capacity is lower than this
# value and a event of Battery Low will be identified. The unit is percentage.
# The allowed range is 0 ~ 90. Default is 35 %.
lowbatt-threshold = 35

# A threshold of Remaining Runtime, If the Remaining Runtime is lower than this
# value and a event of Battery Low will be identified. The unit is second.
# The allowed range is 0 ~ 3600. Default is 300 sec.
# Note: When meet this condition the below 'shutdown-sustain' property
# will be ignored.
runtime-threshold = 300

# Enable to run shell script when the event of Battery Low occur.
# The allowed options are yes and no. Default is yes.
lowbatt-active = yes

# Assign a path of script file for event of Battery Low.
# The default is /etc/pwrstatd-lowbatt.sh
lowbatt-cmd-path = /etc/pwrstatd-lowbatt.sh

# How much time in seconds to take script running for event of Battery Low.
# The allowed range is 0 ~ 60. Default is 0 sec.
lowbatt-duration = 0

# Allow Daemon to shutdown system for event of Battery Low.
# The allowed options are yes and no. Default is yes.
lowbatt-shutdown = yes

# Turn UPS alarm on or off.
# The allowed options are yes and no. Default is yes.
enable-alarm = yes

# The necessary time in seconds for system shutdown.
# The UPS will turn power off when this time is expired.
# The allowed range is 0 ~ 3600. Default is 600 sec.(10 min.)
# If the computer shutdown is cause by low runtime condition, the UPS will
# turn power off when the time is expired that time is assigned on
# 'runtime-threshold' property and it is no longer to refer the
# 'shutdown-sustain' property.

shutdown-sustain = 600

# Daemon will turn UPS power off once it ask system shutdown cause by a power
# event. Allowed options are yes and no. Default is yes.
turn-ups-off = yes

# The period of polling UPS in seconds.
# The allowed range is 1 ~ 60. Default is 3 sec.
ups-polling-rate = 3

# the period of re-try to find available UPS in seconds since find nothing at
# last time. The allowed range is 1 ~ 300. Default is 10 sec.
ups-retry-rate = 10

# Prohibiting daemon to provide communication mechanism for client, such as
# pwrstat command. normally, it should be 'no'. It can be 'yes' if any security
# consideration. Allowed options are yes and no. Default is no.
prohibit-client-access = no
# The pwrstatd accepts four types of device node which includes the 'ttyS',
# 'ttyUSB', 'hiddev', and 'libusb' for communication with UPS. The pwrstatd
# defaults to enumerate all acceptable device nodes and pick up to use an
# available device node automatically. But this may cause a disturbance to the
# device node which is occupied by other software. Therefore, you can restrict
# this enumerate behave by using allowed-device-nodes option. You can assign
# the single device node path or multiple device node paths divided by a
# semicolon at this option. All groups of 'ttyS', 'ttyUSB', 'hiddev', or
# 'libusb' device node are enumerated without a suffix number assignment.
# Note, the 'libusb' does not support suffix number only.
#
# For example: restrict to use ttyS1, ttyS2 and hiddev1 device nodes at /dev
# path only.
# allowed-device-nodes = /dev/ttyS1;/dev/ttyS2;/dev/hiddev1
#
# For example: restrict to use ttyS and ttyUSB two groups of device node at
# /dev,/dev/usb, and /dev/usb/hid paths(includes ttyS0 to ttySN and ttyUSB0 to
# ttyUSBN, N is number).
# allowed-device-nodes = ttyS;ttyUSB
#
# For example: restrict to use hiddev group of device node at /dev,/dev/usb,
# and /dev/usb/hid paths(includes hiddev0 to hiddevN, N is number).
# allowed-device-nodes = hiddev
#
# For example: restrict to use libusb device.
# allowed-device-nodes = libusb
allowed-device-nodes =

# Daemon will hibernate system to instead of system shutdown when power
# event occur. Allowed options are yes and no. Default is no.
hibernate = no

# Enable cloud solution.
# The allowed options are yes and no. Default is no.
cloud-active = no

# Account for cloud server login.
cloud-account =

Далее в Docker необходимо установить контейнер, который будет парсить эту информацию и передавать в MQTT брокер:

docker pull dwinks/pwrstat_docker
docker volume create pwrstat_data
docker run -d -p 5003:5003 --name=pwrstat --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v pwrstat_data:/data dwinks/pwrstat_docker

После запуска контейнера смотрим его ID из списка:

docker ps -a
Список запущенных контейнеров

Переходим в консоль контейнера:

docker exec -it a86d79a48027 bash

В контейнере нам нужно для удобства установить nano и отредактировать конфиг. Для это выполняем:

apt update
apt install nano -y
nano /pwrstat.yaml

В конфиге указываем IP адрес и порт MQTT брокера, частота обновления, имя и пароль MQTT.

pwrstat_api: # optional
  log_level: WARNING # optional
mqtt:
  broker: "192.168.0.24"
  port: 1883
  client_id: "pwrstat_mqtt"
  topic: "sensors/basement/power/ups"
  refresh: 5
  qos: 0
  retained: true
  username: "*****" # optional
  password: "*****" # optional, required if username specified
rest:
  port: 5003
  bind_address: "0.0.0.0"
prometheus:
  port: 9222
  bind_address: "0.0.0.0"
  labels:
    rack: "0"

Перезапускаем контейнер:

docker restart pwrstat

Проверяем работоспособность по ссылкам: http://192.168.0.24:5003/health и http://192.168.0.24:5003/mqtthealth. Не забудьте поменять IP на свой. Если все нормально, то мы увидим:

{"code":"SUCCESS","message":"OK"}

Отлично! Данные отправляются в MQTT. Теперь необходимо достать их в Home Assistant. Для этого создаем сенсоры:

mqtt:
  sensor:
    # UPS Sensors
    - name: "UPS Model Name"
      icon: mdi:rename-box
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Model Name'] }}"

    - name: "UPS Rating Voltage"
      icon: mdi:flash-triangle
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Rating Voltage'] }}"

    - name: "UPS Rating Power"
      icon: mdi:lightning-bolt
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Rating Power'] }}"

    - name: "UPS State"
      icon: mdi:transmission-tower
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['State'] }}"

    - name: "UPS Power Supply by"
      icon: mdi:power-socket-eu
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Power Supply by'] }}"

    - name: "UPS Utility Voltage"
      icon: mdi:flash-triangle-outline
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Utility Voltage'] }}"

    - name: "UPS Output Voltage"
      icon: mdi:flash-triangle-outline
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Output Voltage'] }}"

    - name: "UPS Battery Capacity"
      icon: mdi:car-battery
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Battery Capacity'] }}"

    - name: "UPS Remaining Runtime"
      icon: mdi:timer-sand
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Remaining Runtime'] }}"

    - name: "UPS Load"
      icon: mdi:lightning-bolt
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Load'] }}"

    - name: "UPS Line Interaction"
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Line Interaction'] }}"

    - name: "UPS Test Result"
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Test Result'] }}"

    - name: "UPS Last Power Event"
      state_topic: "sensors/basement/power/ups"
      value_template: "{{ value_json['Last Power Event'] }}"

У меня это выглядит вот так:

Список датчиков UPS

Так же я настроил уведомления в Telegramm при отключении и включении электричества.

- alias: "Уведомления - Питание от ИБП"
  mode: single
  trigger:
    - platform: template
      value_template: "{{ states('sensor.ups_state') == 'Power Failure' }}"
  action:
    - service: notify.me
      data:
        message: "Внимание! Проблемы с питанием, переход на питание от аккумулятора."

- alias: "Уведомления - Питание от сети"
  mode: single
  trigger:
    - platform: template
      value_template: "{{ states('sensor.ups_state') == 'Normal' }}"
  action:
    - service: notify.me
      data:
        message: "Питание восстановлено"

На этом настройка завершена! Спасибо за внимание 🙂

Автоматическое удаление записей в ZoneMinder

Автоматическое удаление записей в ZoneMinder

Автоматическое удаление записей в  ZoneMinder

Сколько не искал как настроить ZM, чтобы он сам удалял старые записи, так и не смог найти. Пришлось сделать свой костыль на PHP. Скрипт писался исключительно для себя, и не претендует на правильность.

<?php
$servername = ""; // IP адрес MySQL сервера
$database = "zm"; // Имя БД
$username = ""; // Имя пользователя БД
$password = ""; // Пароль пользователя БД
$path = ""; // Путь к папке с записями
$interval = "7"; // Удалять записи старше N дней

// Создаем соединение
$mysqli = new mysqli($servername, $username, $password, $database);

// Получаем записи старше N дней
$result = $mysqli->query('SELECT * FROM `Events` WHERE StartDateTime < (NOW() - INTERVAL '.$interval.' DAY)');
$rows = $result->fetch_all(MYSQLI_ASSOC);

// Форматируем дату
$arrDates = array();
foreach($rows as $row) {
    $date = explode(' ', $row['StartDateTime'])[0];
    array_push($arrDates, $date);
}
// Удаляем дубликаты дат
$arrDates = array_unique($arrDates);

// Удаляем папки старше N дней
foreach($arrDates as $date) {
    // Удаление для каждого монитора отдельно
    exec('rm -R '.$path.'5/'.$date);
    exec('rm -R '.$path.'6/'.$date);
}

// Удаляем записи старше N дней
$mysqli->query('DELETE FROM `Events` WHERE StartDateTime < (NOW() - INTERVAL '.$interval.' DAY)');

// Закрываем соединение
$mysqli->close();

Скрипт запускается командой php script.php из консоли и может быть добавлен в cron.


Возвращаем rc.local в Ubuntu

Возвращаем rc.local в Ubuntu

В новых версиях Ubuntu убрали rc.local. Но так как я уже привык к нему, а через crontab не очень удобно, то я решил вернуть его обратно.
Создаем файл rc.local:

nano /etc/rc.local

Добавляем в rc.local содержимое:

#!/bin/sh -e 

exit 0 

Даем права на исполение:

chmod +x /etc/rc.local

Создаем rc-local.service:

nano /etc/systemd/system/rc-local.service

И добавляем следующее:

[Unit]
 Description=/etc/rc.local Compatibility
 ConditionPathExists=/etc/rc.local

[Service]
 Type=forking
 ExecStart=/etc/rc.local start
 TimeoutSec=0
 StandardOutput=tty
 RemainAfterExit=yes
 SysVStartPriority=99

[Install]
 WantedBy=multi-user.target

Включаем автозагрузку и запускаем сервис:

systemctl enable rc-local.service
systemctl start rc-local.service

Проверяем, все-ли впорядке:

systemctl status rc-local.service

В случае успеха вывод будет таким:

rc-local.service - /etc/rc.local Compatibility
     Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: enabled)
    Drop-In: /usr/lib/systemd/system/rc-local.service.d
             └─debian.conf
     Active: active (exited) since Mon 2021-04-26 21:27:57 UTC; 24min ago
      Tasks: 0 (limit: 4232)
     Memory: 0B
     CGroup: /system.slice/rc-local.service

Как дать права юзеру на папку в Linux

Как дать права юзеру на папку в Linux

Для начала необходимо создать группу, и добавить в нее нашего юзера.

sudo groupadd dev
sudo usermod -aG dev boris

Далее надо изменить группу для нашей папки

sudo chown -R root:dev /var/www

И соответственно выставить на папку права для группы

sudo chmod -R 755 /var/www

Теперь пользователь boris имеет право чтение/запись файлов в директории /var/www. Что бы дать еще одному пользователю доступ, достаточно будет просто добавить его в группу.


Настройка sendmail + opendkim на Ubuntu

Настройка sendmail + opendkim на Ubuntu

настройка sendmail opendkim ubuntu

Для начала необходимо установить сам sendmail.

apt install sendmail

Редактируем hosts

nano /etc/hosts

и добавляем туда строку

127.0.0.1 localhost domain.com

Обратите внимание! Везде где указан domain.com — меняем на свой реальный домен.

Ставим opendkim и opendkim-toolsе

apt-get install opendkim opendkim-tools

Создаем папку где будут храниться ключи

mkdir -p /etc/opendkim/keys/domain.com

Генерируем ключи

opendkim-genkey -D /etc/opendkim/keys/domain.com -d domain.com -s mail

После выполнения команды в папке /etc/opendkim/keys/domain.com появятся два файла mail.txt и mail.private
Для них необходимо назначить права доступа

chown -R opendkim:opendkim /etc/opendkim/keys/domain.com
chmod 640 /etc/opendkim/keys/domain.com/mail.private
chmod 644 /etc/opendkim/keys/domain.com/mail.txt

Редактируем конфигурацию opendkim

nano /etc/opendkim.conf

Удаляем все и вставляем данную конфигурацию.

AutoRestart Yes
AutoRestartRate 10/1h
UMask 002
Syslog yes
SyslogSuccess Yes
LogWhy Yes
Canonicalization relaxed/simple
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
Mode sv
PidFile /var/run/opendkim/opendkim.pid
SignatureAlgorithm rsa-sha256
UserID opendkim:opendkim
Socket inet:8891@127.0.0.1
Domain domain.com
Selector mail

Создаем три файла и добавляем в них необходимую конфигурацию

nano /etc/opendkim/KeyTable
mail._domainkey.domain.com domain.com:mail:/etc/opendkim/keys/domain.com/mail.private
nano /etc/opendkim/SigningTable
*@domain.com mail._domainkey.domain.com
nano /etc/opendkim/TrustedHosts
127.0.0.1
localhost
domain.com

Открываем конфиг sendmail

nano /etc/mail/sendmail.mc

и добавляем в конец

INPUT_MAIL_FILTER(`opendkim', `S=inet:8891@127.0.0.1')

Открываем конфиг opendkim

nano /etc/default/opendkim

и расскоментируем строку SOCKET=»inet:8891@localhost«
Если ее нету, то просто добавляем в конец.

Пересобирем конфиг opendkim и перезапускаем службы

sendmailconfig
systemctl restart sendmail
systemctl restart opendkim

Теперь необходимо добавить TXT запись домена.
Субдомен указываем mail._domainkey, а значение берем из файла /etc/opendkim/keys/domain.com/mail.txt
Пример:

v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1hrOt283HUPBskuF2f6Dij9BDxxx+Gz09lGUu7dp8lL8S7it3SI2HQRqgM/HlsNoRihNxxx/Edgs8FFoaru4N7b8QXJirXxpe7yJRbnUhYacFVxxxGuFJPpRr6LNSVWhWh8d4Z2e2af2n2VJVbb5ByP3b6MxxxKYw8gj1oVyHVq+kM7jLzOvA8zu9NyPaPhKcF68ZVCnkrON4lgUzQwLwJHo3I5YF7zwAK5jDRZlriI8aXkzxxxSYlyzMFyd6E/5s9TwO40UyhfEl3WooLRaB27ty5MBdg4pJtch4IiM4FhslxRDf1d7juuDktL81YdDosD4RDBgphl6lZoOynOe9wIDAQAB

На этом настройка sendmail + dkim завершена. Проверить работоспособность dkim можно на https://www.mail-tester.com/