Мониторинг и управление APC Smart UPS 2000

Реанимирую тему (опять) :slight_smile:
Есть APC Smart UPS 2000 (surt2000xli) и желание мониторить (а, может, и управлять) по RS-232 (с выводом на какие-нибудь виртуальные устройства в WB).

Вопросы:

  1. Правильно понимаю, что там нужен какой-то проприетарный кабель (или спец распайка), а не стандартный COM-USB или COM-COM шлейф?
  2. Какой есть способ физический подключить его к WB6 ?
  3. Какой способ программно есть работать с этим интерфейсом? Упомянули Linux NUT и интеграцию с MQTT - речь о том, что с помощью стандартных пакетов Linux можно организовать публикацию (двустороннюю) в MQTT из RS-232 ИБП? Есть какие-то ссылки, где почитать/посмотреть?
  4. Это все поддерживается на самом WB/Debian или нужен отдельный Linux ПК?

Спасибо!

Чуть скорректирую запрос - интересует подключение именно через RS-232, а не сетевую карту (за неимением оной).

Протокол можно поддержать и с помощью wb-mqtt-serial RPC

Добрый день!

Релевантного опыта работы с данным ИБП у меня нет, но, ознакомившись с документацией, могу предложить следующее:

  1. Кабель для подключения:
    Для подключения ИБП APC Smart-UPS через RS-232 требуется специальный кабель с определённой распайкой. Стандартные кабели COM-USB или COM-COM не подойдут. APC использует проприетарные кабели, такие как кабели APC:
  • 940-0024C,
  • 940-0024B,
  • 940-0024D,
  • 940-1524.
    Чаще всего нужный кабель входит в поставку ИБП.
  1. **Подключение к контроллеру WB:
  • В контроллер WB можно установить модуль расширения RS-232 и подключить к контроллеру через него.
  • Если у данной модели ИБП есть USB-порт, можно подключиться через него, установив соответствующие драйверы, в контроллере установлен стандартный Debian.
  1. Для мониторинга и управления ИБП в Linux широко используется пакет Network UPS Tools (NUT). NUT поддерживает множество моделей ИБП и собирает данные об их состоянии. Для интеграции с MQTT можно использовать инструменты, такие как nut-to-mqtt, которые экспортируют данные из NUT в MQTT-брокер.
2 лайка

Добрый день!

С совершенно отвратительными бубнами заставил работать:
NUT + поделку с GitHub nut2mqtt. В итоге получил сообщения в MQTT:

А как мне теперь использовать их в устройствах WB ? Т.е., в идеале получить настроенное устройство, которое можно увидеть в разделе “Устройства” (Devices) и чтоб писался в БД WB график истории полученных значений.

В целом, я нашел, наверное, доку - Подключение кондиционера через MQTT
Но есть вопрос. В треде сказано: что можно/правильно “переписать прошивку для того чтобы она создавала топики MQTT сразу в соответствии с конвенцией”.
А если публикацию перерисать так, чтобы она укладывалась в конвенцию, что это даст? Где тогда будет происходить привязка к виртуальному (или как-то еще объявленному устройству)?

В общем, удалось запустить и связать ИБП c виртуальным устройством WB , делюсь инструкцией и скриптами, вдруг , кому пригодится.

1. Раздобыть или спаять кабель по схеме вашей модели, самый распространенный (и подходит на всю серию APC SURT) 940-024C:


2. Настроить Docker (если не настроен уже)

3. Создать (где удобно) директорию и файл конфигурации docker-compose.yml (от root пользователя):

docker-compose.yml

services:
  app:
    image: ${REGISTRY_URI:-instantlinux}/nut-upsd:latest
    restart: always
    environment:
      API_USER: apcmon #произвольный пользователь к API
      API_PASSWORD: apcmon #произвольный пароль к API
      DRIVER: apcsmart #ваш драйвер ИБП из https://networkupstools.org/stable-hcl.html
      NAME: APC #произвольное имя устройства
      PORT: /dev/ttyUSB0 #ваш порт! Сразу при подключении USB можно глянуть dmseg | grep tty
      SDORDER: -1 #отключение автовыключения ИБП
    ports:
    - ${PORT_UPSD_1:-3493}:3493
    privileged: true

  nut:
    image: 2mqtt/nut:0.0.3
    restart: always
    environment:
      - MQTT_ID=nut
      - MQTT_PATH=nut #название раздела в MQTT, куда будет публикация
      - MQTT_HOST=mqtt://192.168.2.54 #IP адрес MQTT сервиса, совпадает с адресом контроллера WB
      - MQTT_USERNAME=mqttuser #пользователь для авторизации в MQTT, если настроена авторизация в mosquitto
      - MQTT_PASSWORD=mqttuserpwd #пароль для авторизации в MQTT, если настроена авторизация в mosquitto
      - NUT_HOST=192.168.2.54 #IP адрес сервера, на котором развернут NUT, т.е. локальный адрес данного сервера
      - NUT_USERNAME=apcmon #должен соответствовать пользователю NUT API_USER
      - NUT_PASSWORD=apcmon #должен соответствовать паролю NUT API_PASSWORD
      - NUT_INTERVAL=60000 # интервал опроса в мс

5. Поднять контейнеры:

docker compose up -d --build

image

6. Есть нюанс, связанный с тем, что nut2mqtt модуль написан криво, и если его поднимать одновременно с Nut - работать не будет, поэтому надо после поднятия (и при каждой перезагрузке) рестартануть контейнер nut2mqtt:

docker restart nut-nut-1

И вставить это в cron (crontab -e), чтобы срабатывало при перезагрузке:

@reboot /bin/sleep 60 ; sudo docker restart nut-nut-1

7. Настроить конвертер в виртуальное устройство WB
Заходим в настройку правил и добавляем новый файл, process_apc_mqtt.js:

var devName = "APC";
var mqttBase = "nut/APC/";
var regBattery = "battery";
var regInput = "input";
var regOutput = "output";
var regStatus = "status";

defineVirtualDevice(devName, 
                    {title: "Smart-UPS RT 2000 XL",
                     cells: {
                       "input.frequency": {type: "value", "units": "Hz", "title": "Линия.Частота", value: -1, "order": 1},
                       "input.quality": {type: "text", "title": "Линия.Качество", value: 'Н/Д', "order": 2},
                       "input.sensitivity": {type: "text", "title": "Линия.Чувствительность", value: "Н/Д", "order": 3},
                       "input.transferHigh": {type: "value", "units": "V", "title": "Линия.Высокое напряжение переключения", value: -1, "order": 4},
                       "input.transferLow": {type: "value", "units": "V", "title": "Линия.Низкое напряжение переключения", value: -1, "order": 5},
                       "input.transferReason": {type: "text", "title": "Линия.Причина переключения", value: "Н/Д", "order": 6},
                       "input.voltage": {type: "value", "units": "V", "title": "Линия.Напряжение", value: -1, "order": 7},
                       "input.voltageMaximum": {type: "value", "units": "V", "title": "Линия.Макс напряжение", value: -1, "order": 8},
                       "input.voltageMinimum": {type: "value", "units": "V", "title": "Линия.Мин напряжение", value: -1, "order": 9},

                       "battery.alarmThreshold": {type: "value", "units": "min", "title": "Батарея.Тревога по оставшимся минутам", value: -1, "order": 10},
                       "battery.charge": {type: "value", "units": "%", "title": "Батарея.Заряд", value: -1, "order": 11},
                       "battery.chargeRestart": {type: "value", "units": "%", "title": "Батарея.Мин процент заряда для включения", value: -1, "order": 12},
                       "battery.date": {type: "text", "title": "Батарея.Дата замены", value: "Н/Д", "order": 13},
                       "battery.packs": {type: "value", "title": "Батарея.Количество комплектов", value: -1, "order": 14},
                       "battery.packsBad": {type: "value", "title": "Батарея.Количество плохих комплектов", value: -1, "order": 15},
                       "battery.runtime": {type: "value", "units": "s", "title": "Батарея.Запас времени", value: -5, "order": 16},
                       "battery.runtimeLow": {type: "value", "units": "s", "title": "Батарея.Отключение при запасе", value: -1, "order": 17},
                       "battery.voltage": {type: "value", "units": "V", "title": "Батарея.Напряжение", value: -1, "order": 18},
                       "battery.voltageNominal": {type: "value", "units": "V", "title": "Батарея.Номинал напряжения", value: -1, "order": 19},
                       
                       "output.voltage": {type: "value", "units": "V", "title": "Выход.Напряжение", value: -1, "order": 20},
                       "output.voltageNominal": {type: "value", "units": "V", "title": "Выход.Номинал напряжения", value: -1, "order": 21},
                       
                       "status.mode": {type: "text", "title": "Статус.Режим", value: -1, "order": 22},
                       "status.beeper": {type: "switch", "title": "Статус.Сигнал", value: false, "order": 23},
                       "status.temperature": {type: "value", "units": "deg C", "title": "Статус.Температура", value: -1, "order": 24},
                       "status.load": {type: "value", "units": "%", "title": "Статус.Нагрузка", value: -1, "order": 25}
                     }
                    });

function setDevRegister(message) {
  //log.info("name: {}, value: {}".format(message.topic, message.value))
  regBase = /[^/]*$/.exec(message.topic)[0]+'.';
  if (message.value != '') {
    JSON.parse(message.value, function(n, p) {
      curVal = dev[devName+'/'+regBase+n];
      curValType = typeof(curVal);
      if ((n) && (curValType !== 'undefined')) {
        var v;
        switch(curValType) {
          case "number": v = parseInt(p); break;
          case "float": v = parseFloat(p); break;
          case "string": v = p; break;
          case "boolean": v = (p === 'true'); break;
          default: log('unknown curValType = ' + curValType); v = p;
        }
        dev[devName+'/'+regBase+n] = v;
      }
    });
  }
}

trackMqtt(mqttBase + regInput, setDevRegister);
trackMqtt(mqttBase + regBattery, setDevRegister);
trackMqtt(mqttBase + regOutput, setDevRegister);
trackMqtt(mqttBase + regStatus, setDevRegister);

Вуаля, получаем связь:

4 лайка