СЕРГІЙ РЕЗНІЧЕНКО Posted September 10, 2024 Posted September 10, 2024 Существует пошаговая инструкция по настройке NUT? Quote
Max871 Posted March 13 Posted March 13 Всем привет! Сейчас занимаюсь настройкой NUT на Viva KN-1910. В целом всё завелось кроме веба. У меня и на nginx, и на apache при попытки открыть страницу с CGI - автоматически сохраняются файлы. Как я понял - это скрипты и с ними nginx не работает. Поэтому я поставил apache по инструкции ниже, но проблема сохранилась. Что касается инструкции - я готов выложить после того как настрою пошаговую инструкцию. Через консоль управлять ИБП получается, так же поставил клиентскую чать WinNUT - https://github.com/gawindx/WinNUT-Client/releases/tag/2.1.7740.35837/ Через это ПО коннект успешный, автозапуск файлов NUT после рестарта роутера работает. Буду очень благодарен и признателен за помощь! 1 Quote
KyZZMI4 Posted March 25 Posted March 25 Здравствуйте! Можете написать инструкцию по установке-настройке без веба? Мне бы хватило работы с клиентской часть или консолью. Спасибо! Quote
Max871 Posted May 5 Posted May 5 В 25.03.2025 в 23:19, KyZZMI4 сказал: Здравствуйте! Можете написать инструкцию по установке-настройке без веба? Мне бы хватило работы с клиентской часть или консолью. Спасибо! День добрый! Да - готов написать. Только у меня так веб и не заработал Пробовал разные веб-сервера настроить, данные не отображаются. Подготовлю инструкцию завтра и выложу. 1 Quote
Max871 Posted May 11 Posted May 11 В 25.03.2025 в 23:19, KyZZMI4 сказал: Здравствуйте! Можете написать инструкцию по установке-настройке без веба? Мне бы хватило работы с клиентской часть или консолью. Спасибо! Добрый день! Извиняюсь за долгий ответ. Итак: Мной были установлены комоненты NUC как это было написано в самом начале данной темы. Модель моего UPS - CPS Value400EI. Драйвер использовал blazer_ser. - только с ним заработало. На сайте проекта можно посмотреть какой драйвер будет поддерживаться для определённой модели - https://networkupstools.org/stable-hcl.html Моя модель была указана без буквы I (как я понял - это просто модификация). Теперь что касается настроек конфигов (комментарии я не удалял, только либо раскомментировал, либо добавлял строки. Для удобства можно сделать бэкапы). Для редактирования я использовал winSCP и notepad++. Настроил в winSCP открытие текстовых файлов через NP++: /opt/etc/init.d/S15upsd Спойлер #!/bin/sh ENABLED=yes PROCS=upsd ARGS="-u root" PREARGS="" DESC=$PROCS PATH=/opt/sbin:/opt/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin . /opt/etc/init.d/rc.func hosts.config Спойлер MONITOR myups@localhost "CPS Value400EI" nut.config Спойлер MODE=netserver ups.config (номера vendorid и productid надо посмотреть какие в системе отобразились, могут откличатсья) Спойлер [myups] driver = usbhid-ups port = auto desc = "CPS Value400EI" vendorid = 0764 productid = 0501 upsd.users Спойлер [upsadmin] password = пароль actions = SET actions = FSD instcmds = ALL upsmon master [upsuser] password = пароль upsmon slave [upsmonitor] password = пароль upsmon primary Теперь в консоль (например через putty) добавляем в автозапуск S15upsd и запускам через команду upsd. У меня сейчас вот так: Спойлер Network UPS Tools upsd 2.8.1 Fatal error: A previous upsd instance is already running! Используя команду upsc myups@localhost можно увидеть список всех данных бесперебойника и управлять, например, сигналом. Который можно выключить: Спойлер ups.beeper.status: disabled Для подключения к UPS из Windows настроил WinNUT-client. Нужно обратить внимание, что после настройки соединения нужно перезапусить программу, иначе не будет отображать, что коннект успешно установлен. На этом всё. Будут вопросы - пишите! 1 Quote
Max871 Posted October 17 Posted October 17 (edited) У меня получилось подключить веб используя веб-сервер lighttpd (помог настроить ИИ), но вот вкладка "Settings" так и не завелась. Так же я настроил телегам бота на роутере для отправки статусов по заряду и "в сети" себе + установил mosquitto-client-nossl для записи в mqtt топик данных по батарее, и для дальнейшего прокидывания данных на mqtt сервер умного дома Sprut Hub (был написан кастомный шаблон). В целом по данному методу можно прокинуть и в Home Assistance. Начнём по порядку: Телеграм-бот Я опубликовал бота по пути etc/telegram_bot, подключил виртуальное окружение venv (через virtualenv), секреты прописал в .env. Архив с ботом приложил. .env python3 -m pip install virtualenv Спойлер ALLOWED_USERS="тут_указать_user_id либо группу через запятую" TELEGRAM_BOT_TOKEN="token id" UPS_NAME="myups@localhost" #имя ups из конфига nut CHECK_INTERVAL="60" # 1 минута LOW_BATTERY_THRESHOLD="20" # низкий уровень заряда аккумулятора ups LOG_FILE="/opt/etc/telegram_bot/bot.log" #путь до логов bot.py (в коде есть квадратик с вопросом - на самом деле это эмоджи батарейки, не удаляйте ) Спойлер import os import asyncio import subprocess import time from dotenv import load_dotenv from telegram import Bot from telegram.request import HTTPXRequest # Конфигурация load_dotenv() TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") ALLOWED_USERS = {int(id) for id in os.getenv("ALLOWED_USERS").split(",") if id} UPS_NAME = os.getenv("UPS_NAME", "ups@localhost") CHECK_INTERVAL = int(os.getenv("CHECK_INTERVAL", "60")) LOW_BATTERY_THRESHOLD = int(os.getenv("LOW_BATTERY_THRESHOLD", "10")) LOG_FILE = os.getenv("LOG_FILE", "/opt/etc/telegram_bot/bot.log") # Инициализация бота bot = Bot( token=TOKEN, request=HTTPXRequest( connection_pool_size=1, read_timeout=10, write_timeout=10, connect_timeout=10 ) ) # Глобальные переменные last_messages = {} send_lock = asyncio.Lock() critical_notification_sent = False def write_log(message: str): """Логирование с временной меткой""" try: os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True) with open(LOG_FILE, "a", encoding="utf-8") as f: f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {message}\n") except Exception as e: print(f"Ошибка записи лога: {e}") async def parse_ups_status(): """Парсинг статусов ИБП""" try: result = subprocess.run( ["upsc", UPS_NAME], capture_output=True, text=True, timeout=5 ) if result.returncode != 0: write_log(f"Ошибка upsc: {result.stderr.strip()}") return {"status": "ERROR", "message": "🐞 Ошибка запроса к ИБП"} data = {} charge = None status_flags = [] for line in result.stdout.splitlines(): if ":" in line: key, val = line.split(":", 1) key = key.strip() val = val.strip() data[key] = val if key == "ups.status": status_flags = val.split() elif key == "battery.charge": try: charge = int(val) except ValueError: charge = 0 # Анализ статуса is_online = "OL" in status_flags is_charging = "CHRG" in status_flags is_low_batt = "LB" in status_flags is_fsd = "FSD" in status_flags is_on_battery = "OB" in status_flags is_discharging = "DISCHRG" in status_flags # OB DISCHRG LB # Определение статуса # 1. Проверяем заряд в первую очередь if is_charging: if is_low_batt: status = "CHARGING_LOW" message = f"🔌 ИБП в сети, батарея заряжается: 🪫 {charge}%" else: status = "CHARGING" message = f"🔌 ИБП в сети, батарея заряжается: 🔋 {charge}%" # 2. Режим работы от батареи elif is_on_battery: if is_low_batt and charge <= LOW_BATTERY_THRESHOLD: status = "FSD_CRITICAL" message = f"🚨 ИБП критический 🪫: {charge}% ≤ {LOW_BATTERY_THRESHOLD}%" elif is_low_batt: status = "ONBATTERY_LOW" message = f"❗⚠️ ИБП не в сети, 🪫 {charge}%" else: status = "ONBATTERY" message = f"⚠️ ИБП не в сети, 🔋 {charge}%" # 3. Онлайн (без зарядя) elif is_online: status = "ONLINE" message = f"🔌 ИБП в сети, 🔋 {charge}%" # 4. Все остальные случаи else: status = "UNKNOWN" message = f"⁉️ ИБП Неизвестный статус: {' '.join(status_flags)}" return { "status": status, "charge": charge, "flags": status_flags, "message": message, "is_critical": status in ("FSD_CRITICAL", "ONBATTERY_LOW") } except subprocess.TimeoutExpired: return {"status": "TIMEOUT", "message": "⌛ ИБП не отвечает"} except Exception as e: write_log(f"Ошибка парсинга статуса: {e}") return {"status": "ERROR", "message": f"❌ Ошибка мониторинга: {str(e)}"} async def send_alert(chat_id: int, message: str, cooldown: int = 30) -> bool: """Отправка сообщения с защитой от дублей""" global last_messages async with send_lock: now = time.time() last_msg, last_time = last_messages.get(chat_id, (None, 0)) if message == last_msg and now < last_time + cooldown: # write_log(f"Попытка дублирования (заблокировано): chat_id={chat_id}") return False for attempt in range(3): try: await bot.send_message(chat_id=chat_id, text=message) last_messages[chat_id] = (message, now) # write_log(f"Уведомление отправлено в {chat_id}") return True except Exception as e: if attempt == 2: write_log(f"Ошибка отправки в {chat_id}: {e}") await asyncio.sleep(2) return False async def monitor_ups(): """Основной цикл мониторинга""" global critical_notification_sent write_log("===== Бот запущен =====") last_status = None try: while True: ups_data = await parse_ups_status() current_status = ups_data["status"] # Логирование только при изменении if current_status != last_status: status_info = f"Статус: {current_status} | Заряд: {ups_data.get('charge')}%" write_log(status_info) # Обработка критического статуса if ups_data.get("is_critical"): if not critical_notification_sent: for chat_id in ALLOWED_USERS: await send_alert(chat_id, ups_data["message"]) critical_notification_sent = True else: critical_notification_sent = False # Обычные уведомления при изменении статуса if current_status != last_status and "message" in ups_data: for chat_id in ALLOWED_USERS: await send_alert(chat_id, ups_data["message"]) last_status = current_status await asyncio.sleep(CHECK_INTERVAL) except asyncio.CancelledError: write_log("Бот остановлен по запросу") except Exception as e: write_log(f"КРИТИЧЕСКАЯ ОШИБКА: {e}") finally: write_log("===== Бот завершил работу =====") if __name__ == "__main__": try: asyncio.run(monitor_ups()) except KeyboardInterrupt: write_log("Бот остановлен в ручную") Для запуска бота потребуется добавить в init.d файл S90telegram_bot (добавлен в архив) Спойлер #!/bin/sh ENABLED=yes BOT_SCRIPT="/opt/etc/telegram_bot/bot.py" PID_FILE="/var/run/telegram_bot.pid" VENV_ACTIVATE="/opt/etc/telegram_bot/venv/bin/activate" # Функция проверки работает ли процесс is_process_running() { [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null } case "$1" in start) if is_process_running; then echo "Бот уже запущен (PID $(cat $PID_FILE))" exit 1 fi # Очистка предыдущего PID rm -f "$PID_FILE" # Запуск в виртуальном окружении . "$VENV_ACTIVATE" python "$BOT_SCRIPT" & echo $! > "$PID_FILE" echo "Бот запущен (PID $(cat $PID_FILE))" ;; stop) if is_process_running; then kill -9 $(cat "$PID_FILE") rm -f "$PID_FILE" echo "Бот остановлен" else echo "Бот не запущен" fi ;; restart) $0 stop sleep 2 $0 start ;; status) if is_process_running; then echo "Бот работает (PID $(cat $PID_FILE))" else echo "Бот не запущен" fi ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 ;; esac Интеграция в умный дом Для отправки сообщений в умный дом я воспользовался пакетом mosquitto-client-nossl. Был создан файл для записи данных в mqtt - ups_mqtt_report.sh и добавлен в папку крона "cron.1min" - для запуска раз в минуту. Содержание файла: Спойлер #!/bin/sh UPS_NAME="myups" MQTT_HOST="192.168.X.X" MQTT_PORT= #опционально, если будет логин/пароль MQTT_USER="mqtt" MQTT_PASS="mqtt" CHARGE=$(upsc "$UPS_NAME" battery.charge 2>/dev/null) [ -n "$CHARGE" ] && mosquitto_pub -r -i ups-client -h "$MQTT_HOST" -p "$MQTT_PORT" \ -u "$MQTT_USER" -P "$MQTT_PASS" \ -t "ups/battery/charge" -m "$CHARGE" Для получения сообщений прописываем команду Спойлер mosquitto_sub -h 192.168.X.X -p 000 -u mqtt -P mqtt -i ups-monitor -t "ups/battery/charge" -v где 000 - ваш порт mqtt брокера В Sprut Hub я добавил кастомный шаблон устройства для UPS: Спойлер { "name": "CPS Value400EI", "manufacturer": "CPS", "model": "Value400EI", "modelId": "(ups)/battery/charge", "catalogId": 0, "services": [ { "type": "BatteryService", "characteristics": [ { "type": "BatteryLevel", "link": [ { "type": "Integer", "topicGet": "(1)/battery/charge" } ] } ] } ] } Новое устройство добавил через контроллер mqtt, добавил виртуальный датчик "батарейка" и связал её с UPS. После чего можно использовать заряд UPS для автоматизации. telegram_bot.zip Edited October 25 by Max871 Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.