MQTT bridge + web UI

Доброго дня!
Есть 5 контроллеров WB7 из которых один является головным.
Настроил на головном контроллере бриджи на все остальные контроллеры, пробовал 2 варианта.

Вариант 1. С размещением данных в корень

# wb-02
connection wb_2
address 192.168.1.48
remote_username wb-02
remote_password ********************
notifications true
notification_topic /client/wb_2/bridge_status
keepalive_interval 20
restart_timeout 20
topic /# in 0

Вариант 2. Считаю более верным, с размещением данных в отдельную директорию с названием контроллера откуда мы их забираем.

#wb-03
connection wb_3
address 192.168.1.52
remote_username wb-03
remote_password ********************
notifications true
notification_topic /client/wb_3/bridge_status
keepalive_interval 20
restart_timeout 20

topic /# in 0 /wb03 /devices

По итогу в первом варианте вижу в Web панели датчики температур кроме IO, во втором варианте вообще ничего в панели не вижу.

Через MQTT Explorer я все топики наблюдаю.

В Web панели топиков не вижу.

Отображается только, то что добавлено в корень и то не все (скорее всего это не правильно), как добавить отображение устройств и топиков в Web панель? Чтобы в можно было забирать данные в панели вида /wb$/# (где $ номер slave контроллера), требуется для настройки дашброда.

Добрый день!

Чтобы наладить отображение топиков в веб-интерфейсе при использовании MQTT-моста, убедитесь, что настройки выполнены корректно. Ниже приведены основные рекомендации:

  1. Проверьте указание топиков в настройках моста.
    Убедитесь, что в конфигурации указаны именно те топики, которые необходимо перенаправлять. Неверные или слишком общие топики могут не отображаться.
  2. Убедитесь, что префиксы заданы правильно.
    Добавленные префиксы в конфигурации моста должны соответствовать структуре, которую ожидает веб-интерфейс. Ошибки в префиксе могут привести к тому, что устройства не будут видны.
  3. Проверьте подписку веб-интерфейса на топики.
    Веб-интерфейс Wiren Board отображает только те устройства и каналы, которые присутствуют в настройках MQTT Channels. Убедитесь, что контроллер подписан на нужные топики.
  4. Сравните поведение с MQTT Explorer.
    Если топики видны в MQTT Explorer, но не отображаются в веб-интерфейсе, скорее всего, проблема в отображении или структуре топиков, не распознаваемой интерфейсом.

Подробности по настройке моста описаны в документации:
https://wirenboard.com/wiki/MQTT#Настройка_MQTT_моста_(bridge)

Документацию уже несколько раз всю прошерстил и пробовал различные варианты передачи топиков, при настроеном бридже нужно ли подписываться на топик головным контроллером? (п.3)

  1. /wb_02/devices - забираем сюда данные из подчиненных контроллеров, вот это дело хотелось бы увидеть в MQTT Channels.
  2. /devices и все дефолтные топики основного контроллера - оставляем без изменений.

Собственно не получается увидеть MQTT Channels и веб интерфейсе топки вида /контроллер_№/devices/# пока не очень понимаю как это сделать, буду рад если поможете с этим.

# wb-02
connection wb_2
address 192.168.1.48
remote_username wb-02
remote_password *****************
notifications true
notification_topic /client/wb_2/bridge_status
keepalive_interval 20
restart_timeout 20
#topic /wb02/# in 0
**topic /devices/# in 0 /devices/wb_02**

В такой конфигурации получилось увидеть устройства в MQTT Channels

Однако в устройства и в виджеты почему то залетело одним куском.

Добрый день!

Топик формируется некорректно — не происходит разделения по устройствам, из-за чего данные не отображаются должным образом.

Рекомендую ознакомиться с данной статьёй и привести структуру топиков к требуемому формату.

С датчиками температуры разобрался, их получается возможным прокинуть, а вот входы и выходы GPIO не выходит, так как у головного контроллера топики в каталоге wb-gpio полностью повторяют топики на других контроллерах.

Можно ли как то обойти эту коллизию, например переименовать топики IO на суб.контроллерах? К примеру к наименованием входов/выходов добавить суффикс с именем или ID контроллера?

Добрый день!

Да, вы можете создать виртуальные устройства с нужными названиями. Это самый простой способ убрать коллизию.

Спасибо, данный вариант подходит, в планах переименовать все IO входы/выходы на конечных контроллерах, а далее подписаться на них через бридж.

В данной схеме виртуальное устройство постит MQTT топик и отображается в Web UI.

Единственное пока не разобрался, почему то значения контрола IO не постится в ранее созданное виртуальное устройство.

defineVirtualDevice('wb02', {
    title: {en: 'Controller WB02', ru: 'Контроллер WB02'} ,
    cells: {
      EXT1_IN1: {
        readonly: true,
        title: "EXT1_IN1",
        type: "switch",
        value: false
      },
       EXT1_IN2: {
        readonly: true,
        title: "EXT1_IN2",
        type: "switch",
        value: false
      },
    }
});


defineRule({
  whenChanged: "wb-gpio/EXT1_IN1",
  then: function (newValue, devName, cellName) {
	dev["wb02/EXT1_IN1"] = newValue;

  }
});

defineRule({
  whenChanged: "wb-gpio/EXT1_IN2",
  then: function (newValue, devName, cellName) {
	dev["wb02/EXT1_IN2"] = newValue;

  }
});

Код элементарный, возможно здесь неуместно использовать whenChanged, так как правило срабатывает при изменении.

Буду рад если натолкнете в нужное направление, значение контрола wb-gpio/EXT1_IN1 не присваивается контролу wb02/EXT1_IN1.

Добрый день!

Если подписаться на соответствующие топики, данные поступают?

В общем все не так просто как оказалось, с помощью документации и нейросетей удалось набросать некий костыль, который создает и синхронизирует GPIO устройства с виртуальными устройствами.

Единственное с чем не разобрался, так это с сортировкой GPIO виртуальных устройств, которые создаются циклом из массива, почему то в Web UI сортировка рандомная, хотя в консоли вроде все отрабатывает нормально.

Еще под вопросом отслеживание изменений в функции где задействован whenChanged, пока не тестировал, позже отпишусь.

var inputs = [
    'A1_OUT', 'A2_OUT', 'A3_OUT', 'A4_OUT',
    'A1_IN', 'A2_IN', 'A3_IN', 'A4_IN',
    '5V_OUT', 'V_OUT',
    'EXT1_IN1', 'EXT1_IN2', 'EXT1_IN3', 'EXT1_IN4',
    'EXT1_IN5', 'EXT1_IN6', 'EXT1_IN7', 'EXT1_IN8',
    'EXT1_IN9', 'EXT1_IN10', 'EXT1_IN11', 'EXT1_IN12',
    'EXT1_IN13', 'EXT1_IN14'
];

// Создаем виртуальное устройство

defineVirtualDevice('wb02', {
    title: {en: 'Controller WB02-1', ru: 'Контроллер WB02-1'},
    cells: (function() {
        var cells = {};
        for (var i = 0; i < inputs.length; i++) {
            cells[inputs[i]] = {
                readonly: true,
                title: inputs[i],
                type: "switch",
                value: false
            };
        }
        
        return cells;
    })()
});

/* 
defineVirtualDevice('wb02', {
    title: {en: 'Controller WB02', ru: 'Контроллер WB02'},
    cells: (function() {
        // Создаем массив в нужном порядке
        var orderedCells = [];
        for (var i = 0; i < inputs.length; i++) {
            orderedCells.push({
                name: inputs[i],
                params: {
                    readonly: true,
                    title: inputs[i],
                    type: "switch",
                    value: false
                }
            });
        }
        
        // Преобразуем в объект с сохранением порядка
        var cells = {};
        for (var j = 0; j < orderedCells.length; j++) {
            cells[orderedCells[j].name] = orderedCells[j].params;
        }
        
        return cells;
    })()
});
*/

// Функция синхронизации
function syncGpio(inputDev, outputDev) {
    var value = dev[inputDev];
    dev[outputDev] = (value === true || value === 1 || value === "1");
    log("Синхронизировано: " + inputDev + " → " + outputDev + " = " + dev[outputDev]);
}

// Инициализация и создание правил для ВСЕХ устройств
for (var i = 0; i < inputs.length; i++) {
    (function(index) {
        var inputName = inputs[index];
        var inputDev = "wb-gpio/" + inputName;
        var outputDev = "wb02/" + inputName;
        
        // Логирование перед синхронизацией
        log.info("Обработка устройства: ", inputName, 
                " Физический адрес: ", inputDev,
                " Виртуальный адрес: ", outputDev);
        
        // Инициализация состояния
        syncGpio(inputDev, outputDev);
        
        // Создание правила для КАЖДОГО устройства (без фильтрации)
        defineRule("gpioSync_" + inputName.replace(/\//g, '_'), {
            whenChanged: inputDev,
            then: function(newValue) {
                syncGpio(inputDev, outputDev);
            }
        });
    })(i);
}

Результат работы скрипта.

Добрый день!

Сортировка осуществляется в алфавитном порядке. Если добавить к названиям префиксы вроде 001, 002 и т.д., элементы будут отображаться в нужной последовательности.

Понял, попробую, спасибо.
Еще вопрос, можно ли как то вычитывать содержимое gpio?
Чтобы руками не объявлять массив с перечислением названий I/O входов/выходов.

wb01 исходный контроллер, wb02 тот с которого собираем данные при помощи виртуальных устройств, теоретически сортировка отработать должна …

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

Что касается wb02 — сортировка работает в ожидаемом порядке.

Это касается скрипта для wb-rules, в данный момент массив объявляется вручную, хотел бы уточнить, можно ли как то забирать названия контактов IO, чтобы не объявлять его вручную.

var inputs = [
    'A1_OUT', 'A2_OUT', 'A3_OUT', 'A4_OUT',
    'A1_IN', 'A2_IN', 'A3_IN', 'A4_IN',
    '5V_OUT', 'V_OUT',
    'EXT1_IN1', 'EXT1_IN2', 'EXT1_IN3', 'EXT1_IN4',
    'EXT1_IN5', 'EXT1_IN6', 'EXT1_IN7', 'EXT1_IN8',
    'EXT1_IN9', 'EXT1_IN10', 'EXT1_IN11', 'EXT1_IN12',
    'EXT1_IN13', 'EXT1_IN14'
];

Парсить топик или dev устройства. Пробовали парсить wb-gpio, но не удалось.

Цель автоматизировать сбор входных данных, в случае добавления еще одного блока IO входов и выходов.

Вариант с префиксом от 1 до N

defineVirtualDevice('wb02', {
    title: {en: 'Controller WB02', ru: 'Контроллер WB02'},
    cells: (function() {
        var cells = {};
        for (var i = 0; i < inputs.length; i++) {
            // Добавляем нумерационный префикс (двузначный, с ведущим нулем)
            var numberedTitle = (i+1 < 10 ? '0' : '') + (i+1) + '. ' + inputs[i];
            
            cells[inputs[i]] = {
                readonly: true,
                title: numberedTitle,  // Используем название с префиксом
                type: "switch",
                value: false,
                // Добавляем порядковый номер для сортировки
                order: i  
            };
        }
        return cells;
    })()
});

Сортировка вроде отработала нормально.

1 Like

Насколько мне известно, только руками можно.

Вас понял, было бы неплохо если бы это в будущем доработали.

P.S.
Скрипт с работающей сортировкой, полный, кому нибудь да пригодится.

var inputs = [
    'A1_OUT', 'A2_OUT', 'A3_OUT', 'A4_OUT',
    'A1_IN', 'A2_IN', 'A3_IN', 'A4_IN',
    '5V_OUT', 'V_OUT',
    'EXT1_IN1', 'EXT1_IN2', 'EXT1_IN3', 'EXT1_IN4',
    'EXT1_IN5', 'EXT1_IN6', 'EXT1_IN7', 'EXT1_IN8',
    'EXT1_IN9', 'EXT1_IN10', 'EXT1_IN11', 'EXT1_IN12',
    'EXT1_IN13', 'EXT1_IN14'
];


defineVirtualDevice('wb02', {
    title: {en: 'Controller WB02', ru: 'Контроллер WB02'},
    cells: (function() {
        var cells = {};
        for (var i = 0; i < inputs.length; i++) {
            // Создаем имя с префиксом
            var prefixedName = (i+1 < 10 ? '0' : '') + (i+1) + '_' + inputs[i];
            
            cells[prefixedName] = {  // Используем имя с префиксом как ключ
                readonly: true,
                title: prefixedName,  // Отображаемое имя с префиксом
                type: "switch",
                value: false,
                order: i
            };
        }
        return cells;
    })()
});

// Функция синхронизации
function syncGpio(inputDev, outputDev) {
    var value = dev[inputDev];
    dev[outputDev] = (value === true || value === 1 || value === "1");
    log("Синхронизировано: " + inputDev + " → " + outputDev + " = " + dev[outputDev]);
}

// Инициализация и создание правил
for (var i = 0; i < inputs.length; i++) {
    (function(index) {
        // Создаем имена с префиксами для всех устройств
        var prefixedName = (index+1 < 10 ? '0' : '') + (index+1) + '_' + inputs[index];
        var inputDev = "wb-gpio/" + inputs[index];  // Физическое устройство без префикса
        var outputDev = "wb02/" + prefixedName;     // Виртуальное устройство с префиксом
        
        log.info("Обработка устройства:", 
                "\nФизическое: " + inputDev,
                "\nВиртуальное: " + outputDev);
        
        // Инициализация состояния
        syncGpio(inputDev, outputDev);
        
        // Создание правила
        defineRule("gpioSync_" + prefixedName.replace(/\//g, '_'), {
            whenChanged: inputDev,
            then: function(newValue) {
                syncGpio(inputDev, outputDev);
            }
        });
    })(i);
}
1 Like

Мне кажется, Вы создали себе трудность и теперь её героически преодолеваете.
Давайте по порядку.
Что бы устройство появилось в интерфейсе, наименование его mqtt топика должно соответствовать Конвенции А это ничто иное как:

/devices/имяустройства/controls/имяканала

Я Вам предлагаю на всех устройствах просто переименовать топики согласно конвенции без всяких костылей. Например:
Было: wb-m1w2_83
Стало: wb02_wb-m1w2_83
Это делается в настройках устройства.
И… всё. Все устройства со всех контроллеров появятся.

Ранее уже спрашивал как переименовать, чтобы названия не совпадали, по итогу мне подсказали, что можно сделать это путем создания виртуальных устройств.

Я бы тоже рад не городить костыли, но в конфиге GPIO не нашел где можно суффикс к каждому наименованию IO добавить, ну или хоть как то его переименовать. Было бы хорошо, если бы мне изначально подсказали как это сделать.