Некоторые картинки не загружаются из РФ и РК, используйте VPN.

понедельник, 1 апреля 2024 г.

RPi 3B+ DHT11/22 Monitoring

На данный момент в "серверной" мы проверяем температуру при помощи камеры направленной на термометр от Xiaomi. Работу кондиционера оцениваем по веревочкам, т.е. если они колышатся, то кондиционер работает. Также на самой камере настроено оповещение на почту при наличии движения от этих веревочек два раза в сутки. Логика простая: есть движение - отправить письмо, нет движения - нет письма. Нет письма - реагируем. У меня есть RaspberryPi и датчик температуры/влажности DTH11. Малинка стоит в серверной, так почему бы мне не научить малину считывать температуру? Почему бы не мониторить температуру заббиксом, чтобы оповещать при критической ситуации?

Сначала подключим к малине датчик


Для работы сенсора буду использовать Adafruit, надо установить (предполагается что pip3 и python уже стоят на малине, моя ОС на базе Debian 12):

user@rpi02:~$ sudo pip3 install Adafruit_Python_DHT --break-system-packages

На просторах интернета есть простой скрипт (чуть изменил вывод)

import Adafruit_DHT
import time

# comment and uncomment the lines below depending on your sensor
# sensor = Adafruit_DHT.DHT22
sensor = Adafruit_DHT.DHT11

# DHT pin connects to GPIO 4
sensor_pin = 25

while true:
    humidity, temperature = Adafruit_DHT.read_retry(sensor, sensor_pin)
# Use read_retry to retry in case of failure
    if humidity is not None and temperature is not None:
        temperatureF = temperature * 9/5.0 + 32
        print("{{{{\"tempc\":\"{0:0.1f}\"}},{{\"tempf\":\"{1:0.1f}\"}},{{\"humidity\":\"{2:0.1f}\"}}}}".format(temperature, temperatureF, humidity))
    else:
        print("Error")
    time.sleep(10)

Тестируем:

user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"24.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"23.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"23.0"}}
{{"tempc":"13.0"},{"tempf":"55.4"},{"humidity":"139.0"}}

Хм, какой странный результат, если система будет нам в заббикс отправлять такие данные, то мы сойдем с ума!!!

Тогда я решил переписать скрипт так, чтобы он брал среднее из N значений с T паузами между итерациями:

import Adafruit_DHT
import time
import sys

if len( sys.argv ) != 3:
    print("0")
    sys.exit()

cycles = int(sys.argv[1])
pauseTime= int(sys.argv[2])

# comment and uncomment the lines below depending on your sensor
# sensor = Adafruit_DHT.DHT22
sensor = Adafruit_DHT.DHT11

# DHT pin connects to GPIO 4
sensor_pin = 25
i = 0
humidityT = 0
temperatureT = 0

while i /lt;=  cycles:
    i += 1
    humidity, temperature = Adafruit_DHT.read_retry(sensor, sensor_pin)
# Use read_retry to retry in case of failure
    if humidity is not None and temperature is not None:
        humidityT = humidityT+humidity
        temperatureT = temperatureT+temperature
        temperatureF = temperature * 9/5.0 + 32
        print("{{{{\"tempc\":\"{0:0.1f}\"}},{{\"tempf\":\"{1:0.1f}\"}},{{\"humidity\":\"{2:0.1f}\"}}}}".format(temperature, temperatureF, humidity))
    else:
        humidityT = 0
        temperatureT = 0
        break
    time.sleep(pauseTime)

humidity = humidityT / cycles
temperature = temperatureT / cycles
temperatureF = temperature * 9/5.0 + 32

print("{{{{\"tempc\":\"{0:0.1f}\"}},{{\"tempf\":\"{1:0.1f}\"}},{{\"humidity\":\"{2:0.1f}\"}}}}".format(temperature, temperatureF, humidity))

Проверяем (10 - количество циклов, 2 - пауза в секундах). В скрипте я добавил сброс на ноль, если хоть раз была ошибка.

user@rpi02:~$ /opt/scripts/zabbix/adafruit.py 10 2
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"26.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.0"}}
{{"tempc":"26.0"},{"tempf":"78.8"},{"humidity":"25.1"}}

Последняя строчка показывает влажность 25.1, а первое значение было 26. Вот такой результат мне нравится.

Пробуем получить данные (zabbix_get устанавливается отдельно, указанная команда не требует шифрования, так как на время тестирования оно отключено):

user@zabbix:~$ zabbix_get -s rpi02 -k adafruit[1,2]
Traceback (most recent call last):
  File "/opt/scripts/zabbix/adafruit.py", line 28, in <module>
    humidity, temperature = Adafruit_DHT.read_retry(sensor, sensor_pin)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/

Но я же проверял, скрипт работает и выдает нужные данные!

На самом деле я даже на секунду не задумался, ибо сразу предположил, что проблема в правах пользователя zabbix. Проверил и действительно, достаточно добавить пользователя в группу gpio

user@zabbix:~$ sudo usermod -a -G gpio zabbix

И снова пробуем получить данные

user@zabbix:~$ zabbix_get -s rpi02 -k adafruit[1,2]
ZBX_NOTSUPPORTED: Timeout while executing a shell script.

Решается простым увеличением таймаута ожидания ответа от скриптов:

user@rpi02:~$ sudo echo "Timeout=30" >> /etc/zabbix/zabbix_agentd.conf
user@rpi02:~$ sudo service zabbix-agent restart
user@zabbix:~$ zabbix_get -s rpi02 -k adafruit[1,2]
{{"tempc":"52.0"},{"tempf":"125.6"},{"humidity":"49.0"}}

Проверяем теперь в веб интерфейсе и тут я опять получаю ошибку ZBX_NOTSUPPORTED: Timeout while executing a shell script. !!!

Пришлось и в конфиге сервера указать таймаут

user@яфиишч:~$ sudo echo "Timeout=30" >> /etc/zabbix/zabbix_server.conf
user@zabbix:~$ sudo service zabbix-server restart

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


Проверил в консоли

user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py 1 1
{{"tempc":"52.0"},{"tempf":"125.6"},{"humidity":"53.0"}}
user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py 1 1
{{"tempc":"52.0"},{"tempf":"125.6"},{"humidity":"52.0"}}
user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py 1 1
{{"tempc":"52.0"},{"tempf":"125.6"},{"humidity":"52.0"}}
user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py 1 1
{{"tempc":"52.0"},{"tempf":"125.6"},{"humidity":"50.0"}}
user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py 1 1
{{"tempc":"52.0"},{"tempf":"125.6"},{"humidity":"46.0"}}
user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py 2 1
{{"tempc":"39.0"},{"tempf":"102.2"},{"humidity":"34.5"}}
user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py 2 1
{{"tempc":"39.0"},{"tempf":"102.2"},{"humidity":"40.0"}}

Да, данные странные и не соответствуют действительности, ибо 52 градуса в помещении где открытая стойка занимает 1/25 всего объема привела бы к нагреву процессоров до максимального значения


Дальше настраиваем заббикс. Он у меня 4.4, поэтому опишу шаблон сам.

Создаю отдельную конфигурацию для заббикс агента

user@rpi02:~ $ cat /etc/zabbix/zabbix_agentd.conf.d/adafruit.conf
# Adafruit_DHT - temperature and humidity sensor
# $1 - number of cycles to obtain the average variable
# $2 - pause before each iteration
UserParameter=adafruit[*],/opt/scripts/zabbix/adafruit.py $1 $2

Записываю скрипт в /opt/scripts/zabbix/adafruit.py.

user@rpi02:~ $ cat /opt/scripts/zabbix/adafruit.py
#!/usr/bin/env python3
# $1 - number of cycles to obtain the average variable
# $2 - pause before each iteration

import Adafruit_DHT
import time
import sys
from gpiozero import CPUTemperature

if len( sys.argv ) != 3:
    print("0")
    sys.exit()

cycles = int(sys.argv[1])
pauseTime= int(sys.argv[2])

# comment and uncomment the lines below depending on your sensor
# sensor = Adafruit_DHT.DHT22
sensor = Adafruit_DHT.DHT11

# DHT pin connects to GPIO 4
sensor_pin = 25
i = 0
humidityT = 0
temperatureT = 0
cpuTemperatureT = 0

while i <=  cycles:
    i += 1
    humidity, temperature = Adafruit_DHT.read_retry(sensor, sensor_pin)
    cpuTemperature = CPUTemperature()
# Use read_retry to retry in case of failure
    if humidity is not None and temperature is not None and cpuTemperature is not None:
        humidityT = humidityT+humidity
        temperatureT = temperatureT+temperature
        cpuTemperatureT = cpuTemperatureT + cpuTemperature.temperature
    else:
        humidityT = 0
        temperatureT = 0
        cpuTemperatureT = 0
        break
    time.sleep(pauseTime)

humidity = humidityT / cycles
temperature = temperatureT / cycles
temperatureF = temperature * 9/5.0 + 32

cpuTemperature = cpuTemperatureT / cycles
cpuTemperatureF = cpuTemperatureT * 9/5.0 + 32

print("{{\"tempc\":\"{0:0.1f}\",\"tempf\":\"{1:0.1f}\",\"humidity\":\"{2:0.1f}\",\"tempcpuc\":\"{3:0.1f}\",\"tempcpuf\":\"{4:0.1f}\"}}".format(temperature, temperatureF, humidity,cpuTemperature,cpuTemperatureF))

Это конечный вид, я изменил опять вывод, так как первый был ошибочный и не подходил для дальнейшей обработки. И добавил еще съем температуры процессора

user@rpi02:~ $ /opt/scripts/zabbix/adafruit.py 1 5
{"tempc":"32.8","tempf":"91.0","humidity":"27.5","tempcpuc":"62.8","tempcpuf":"484.2"}

Параметра у нас три (фаренгейт оставлен по приколу), но запрашивать мы будет не три параметра, а один. Так сам скрипт запускается долго, а если и в несколько потоков его запускать, то будут ошибки. Именно поэтому такой интересный вывод - типа JSON. В самом шаблоне мы настроим зависимые элементы с предобраткой JSONPath. Если система получит 0 вместо JSON, то зависимый элемент будет равен 10C/50F/10%. Данных цифр сложно добиться в моем помещении, поэтому это будет для меня триггером.

Создаем шаблон и добавляем первый элемент. Ключ adafruit[4,5] - 4 итерации с пятисекундными паузами. Для данного датчика есть ограничение, его не рекомендуется опрашивать чаще раза за 1-2 секунды. Запуск скрипта занимает до 5 секунд, стоит учесть это, при выборе количества проверок и размера паузы, нам нужно уложиться в 28 секунд (30 - предел, а еще возможные погрешности)


Первый зависимый элемент. Остальные делаем по аналогии


Добавляем триггер. Условие триггера среднее за 10 минут. Из-за того, что значения плавают, даже со средним из скрипта:


Я решил добавить два триггера и один зависит от другого:


Получилось так:


Можно добавить график по своему вкусу:

В консоли все ОК, а вот такие данные у меня появились в заббиксе после добавления съема температуры процессора

root@rpi02:/home/user# runuser -u zabbix -- /opt/scripts/zabbix/adafruit.py 1 1
2024-03-30 20:06:05 xCreatePipe: Can't set permissions (436) for /home/user/.lgd-nfy0, Operation not permitted
/usr/lib/python3/dist-packages/gpiozero/devices.py:295: PinFactoryFallback: Falling back from lgpio: [Errno 2] No s
  warnings.warn(
{"tempc":"50.0","tempf":"122.0","humidity":"54.0","tempcpuc":"86.9","tempcpuf":"188.5"}
root@rpi02:/home/user# ls -al /home/user/.lgd-nfy0
prw-rw-r-- 1 root root 0 Mar 30 19:09 /home/user/.lgd-nfy0

Опять проблема в правах доступа. Только в заббиксе ссылка идет не на файл /home/user/.lgd-nfy0, а на //.lgd-nfy0. Как выяснилось данный файл создается в каталоге запуска скрипта (не самого скрипта), но почему то с root:root. Поэтому я сделал так:

root@rpi02:/home/vint# cd /
root@rpi02:/# runuser -u zabbix -- /opt/scripts/zabbix/adafruit.py 1 1
2024-03-30 20:31:21 xCreatePipe: Can't set permissions (436) for //.lgd-nfy0, No such file or directory
/usr/lib/python3/dist-packages/gpiozero/devices.py:295: PinFactoryFallback: Falling back from lgpio: [Errno 2] No such file or directory: '.lgd-nfy-3'
  warnings.warn(
{"tempc":"52.0","tempf":"125.6","humidity":"58.0","tempcpuc":"88.0","tempcpuf":"190.4"}
root@rpi02:/# ls -la .lgd-nfy0
ls: cannot access '.lgd-nfy0': No such file or directory
root@rpi02:/# /opt/scripts/zabbix/adafruit.py 1 1
{"tempc":"50.0","tempf":"122.0","humidity":"55.0","tempcpuc":"86.4","tempcpuf":"187.5"}
root@rpi02:/# ls -la .lgd-nfy0
prw-rw-r-- 1 root root 0 Mar 30 20:32 .lgd-nfy0
root@rpi02:/# runuser -u zabbix -- /opt/scripts/zabbix/adafruit.py 1 1
2024-03-30 20:33:16 xCreatePipe: Can't set permissions (436) for //.lgd-nfy0, Operation not permitted
/usr/lib/python3/dist-packages/gpiozero/devices.py:295: PinFactoryFallback: Falling back from lgpio: [Errno 2] No such file or directory: '.lgd-nfy-3'
  warnings.warn(
{"tempc":"37.0","tempf":"98.6","humidity":"169.0","tempcpuc":"89.1","tempcpuf":"192.4"}
root@rpi02:/# chown zabbix:zabbix .lgd-nfy0
root@rpi02:/# runuser -u zabbix -- /opt/scripts/zabbix/adafruit.py 1 1
{"tempc":"50.0","tempf":"122.0","humidity":"54.0","tempcpuc":"88.6","tempcpuf":"191.4"}

Поясню. Сначала перешел в корень и проверил запуск там. При этому файл не создался. Тогда я запустил скрипт от текущего пользователя. Файл создался. Проверил скрипт, он по прежнему выдал ошибку (естественно). Поэтому я поменял владельца файла и теперь под пользователем zabbix исправно отрабатывает. Единственно если запустить скрипт от другого пользователя, то права перепишутся.

Я это делаю в свободном режиме и на данный момент у меня есть проблема:


По какой то причине часть запросов не отрабатывает. Предположим, что скрипт и вправду не успевает, тогда сделаем по другому. Исправим питон скрипт, теперь он будет писать в файл, а не на экран (Исправил только последнюю строчку).

print("{{\"tempc\":\"{0:0.1f}\",\"tempf\":\"{1:0.1f}\",\"humidity\":\"{2:0.1f}\",\"tempcpuc\":\"{3:0.1f}\",\"tempcpuf\":\"{4:0.1f}\"}}".format(temperature, temperatureF, humidity,cpuTemperature,cpuTemperatureF), file=open('/opt/scripts/zabbix/adafruit.log','w'))

Создадим sh скрипт, который будет возвращать содержимое файла как значение элемента узла сети заббикса, и запускать в фоне скрипт питон (& после команды), чтобы агент заббикс не ждал завершения

#!/bin/bash
logFile="/opt/scripts/zabbix/adafruit.log"
if [ -f $logFile ]
then
        content=`cat $logFile`
        if [[ "$content" = "" ]]
        then
                echo "0"
        else
                echo $content
        fi
fi
echo "" > $logFile
check=`pgrep -u zabbix -f adafruit.py >/dev/null && echo 1 || echo 0`
if [[ "$check" = "0" ]]
then
        /opt/scripts/zabbix/adafruit.py $1 $2 &
fi

И поправить в /etc/zabbix/zabbix_agentd.conf.d/adafruit.conf

К сожалению, теперь ни одна итеррация скрипта не укладывается в таймаут по мнению заббикса. Поэтому я решил проверить, а сколько времени занимает работа скрипта, ибо при запуске из консоли работает крайне быстро. Для этого перед и после sh скрипта я добавил вывод в файл отметку времени (неизмененная часть опущена)

#!/bin/bash
debugFile="/opt/scripts/zabbix/adafruit.debug"
echo -e "$(date)\t RUN" >>$debugFile
....
echo -e "$(date)\t END" >>$debugFile

Результат удивил, ибо время выполения скрипта 1 секунда!!!

user@rpi02:~ $ cat /opt/scripts/zabbix/adafruit.debug && tail -n 10 /var/log/zabbix/zabbix_agentd.log
Sun 31 Mar 01:32:46 MSK 2024     RUN
Sun 31 Mar 01:32:46 MSK 2024     END
Sun 31 Mar 01:33:31 MSK 2024     RUN
Sun 31 Mar 01:33:31 MSK 2024     END
Sun 31 Mar 01:34:16 MSK 2024     RUN
Sun 31 Mar 01:34:17 MSK 2024     END
Sun 31 Mar 01:35:01 MSK 2024     RUN
Sun 31 Mar 01:35:01 MSK 2024     END
Sun 31 Mar 01:35:46 MSK 2024     RUN
Sun 31 Mar 01:35:46 MSK 2024     END
249467:20240331:012216.387 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249467:20240331:012331.611 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249465:20240331:012416.440 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249466:20240331:012501.600 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249466:20240331:013001.953 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249467:20240331:013201.830 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249465:20240331:013316.016 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249465:20240331:013401.795 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249466:20240331:013446.967 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.
249466:20240331:013531.127 Failed to execute command "/opt/scripts/zabbix/adafruit.sh 4 5": Timeout while executing a shell script.

Тогда я пошел проверять конфигурационный файл. В него сегодня добавил два параметра, после этого и началась такая кутерьма

 # Range 1-3600 default 5
BufferSend=3600
# Range 2-65535 default 100
BufferSize=2000

Подразумевалось, что данные параметры позволят закэшировать данные на время перезагрузки коммутатора. Закомментировал и данные пошли...

А спустя 10 минут опять сыпет ошибкой таймаута


Вангую, что фокус с фоновым запуском не удался, ибо процесс sh не завершается до всех подпроцессов python. Для дальнейшего изучения добавил вывод отметки времени в конец python скрипта:
t = time.localtime()
current_time = time.strftime("%H:%M:%S", t)
print(current_time, file=open('/opt/scripts/zabbix/adafruit.debug','a'))

И действительно скрипт выполняется в пределах 30 секунд. При этом это 4 итерации с паузой в 5 секунд

user@rpi02:~ $ tail -n 5 /opt/scripts/zabbix/adafruit.debug
Sun 31 Mar 02:07:00 MSK 2024     END
02:07:28
Sun 31 Mar 02:07:31 MSK 2024     RUN
Sun 31 Mar 02:07:31 MSK 2024     END
02:07:59

Полагаю, что где-то ошибка в оценке скорости выполнения скрипта. Поставлю пока 4/4 в настройках элемента узла сети и верну настройки буфера.

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



4/4 тоже бывает по таймауту вылетает. Поставил 4/3

UP 05/04/2024

Поставили DHT22, показания такие же О_О

Комментариев нет:

Отправить комментарий