Главная → Академия → Node-RED: установка, flows, msg/JSON, отладка → Нода `Switch`: маршрутизация сообщений по условиям

Нода `Switch`: маршрутизация сообщений по условиям

Введение в ноду Switch: логический маршрутизатор потоков

В архитектуре потоков Node-RED нода `Switch` выполняет одну из самых критически важных функций — маршрутизация сообщений (message routing). Если представить поток данных как реку, то узел `Switch` — это система шлюзов и каналов, направляющая воду по разным руслам в зависимости от заданных условий. В программировании аналогами этого узла служат условные операторы `if-then-else` или `switch-case`.

Основная задача ноды `Switch` — принять входящий объект `msg` и направить его на один или несколько из своих выходов, основываясь на наборе правил. В отличие от ноды `Change`, которую мы рассматривали в предыдущем уроке, `Switch` по умолчанию не изменяет сообщение. Она лишь принимает решение о его дальнейшей судьбе.

Давайте рассмотрим интерфейс ноды:

  • Property (Свойство): Поле, в котором вы указываете, какую часть объекта `msg` нужно проверить. По умолчанию это `msg.payload`, но можно выбрать `msg.topic`, `flow.variable`, `global.variable` или любое другое свойство, используя синтаксис JSONata.
  • Список правил (Rules): Основная рабочая область, где вы определяете условия. Каждое правило состоит из оператора сравнения (например, "равно", "больше чем", "содержит") и значения, с которым будет сравниваться выбранное свойство.
  • Выходы (Outputs): Для каждого созданного правила автоматически создается соответствующий выход. Вы можете добавить выход `otherwise` (иначе), который сработает, если ни одно из правил выше не будет выполнено.
  • Режим проверки (Checking): Внизу окна настроек находится критически важный переключатель с двумя режимами:
  • * stopping after first match (остановить после первого совпадения): Это режим по умолчанию. Нода проверяет правила последовательно сверху вниз. Как только одно из условий выполняется, сообщение отправляется в соответствующий выход, и дальнейшая проверка прекращается. Это аналог конструкции `if ... else if ... else`.

    * checking all rules (проверять все правила): В этом режиме нода проверяет все правила, и если несколько из них окажутся истинными, копия сообщения будет отправлена в каждый из соответствующих выходов.

    В итоге, нода `Switch` является ключевым инструментом для создания нелинейных, ветвящихся потоков и реализации сложной логики принятия решений. Она позволяет системе реагировать на разные события по-разному, фильтровать ненужные данные и направлять команды только тем исполнительным механизмам, которым они предназначены.

    ---

    Базовая конфигурация: проверка значения msg.payload

    Наиболее распространенный сценарий использования ноды `Switch` — это маршрутизация на основе значения `msg.payload`. Практически любое устройство или сервис передает свое состояние или данные именно в этом свойстве. Это может быть строковая команда от MQTT-кнопки, числовое значение от датчика температуры или булево значение `true`/`false`.

    Процесс настройки прост:

  • В поле `Property` оставляем значение по умолчанию: `msg.payload`.
  • В списке правил добавляем новое правило.
  • Выбираем оператор сравнения из выпадающего списка.
  • В поле справа указываем значение, с которым будет производиться сравнение (строка, число, boolean и т.д.).
  • > 💡 Подсказка: Режим "Остановить после первого совпадения" (stopping after first match) работает как `if ... else if ... else`. Сообщение будет отправлено только в первый совпавший выход. Это наиболее частый и производительный сценарий. Режим "Проверять все правила" (checking all rules) позволяет отправить копию сообщения на все выходы, условия для которых истинны, и используется для более сложных сценариев, например, когда одно событие должно запустить несколько параллельных действий.

    Таблица основных операторов сравнения

    | Оператор | Тип данных | Описание |

    | --------------------- | --------------- | ------------------------------------------------------------------------------------------------------- |

    | is equal to (`==`) | Строка, Число | Правило истинно, если свойство точно равно указанному значению. |

    | is not equal to (`!=`) | Строка, Число | Правило истинно, если свойство не равно указанному значению. |

    | is greater than (`>`) | Число | Правило истинно, если значение свойства (число) больше указанного. |

    | is less than (`<`) | Число | Правило истинно, если значение свойства (число) меньше указанного. |

    | is true / is false| Boolean | Специальные операторы для проверки булевых значений `true` или `false`. |

    | contains | Строка | Правило истинно, если указанная подстрока содержится внутри значения свойства. |

    | is null | Любой | Правило истинно, если свойство `null` или не существует (`undefined`). |

    Практический пример: обработка нажатий беспроводной кнопки

    Представим, что у нас есть беспроводная Zigbee-кнопка, которая публикует в MQTT-топик `devices/buttons/living_room/action` сообщения с разным `payload` в зависимости от типа нажатия:

    • Одинарное нажатие: `msg.payload` = `"single"`
    • Двойное нажатие: `msg.payload` = `"double"`
    • Долгое удержание: `msg.payload` = `"hold"`

    Наша задача — управлять разными группами света в зависимости от нажатия.

    Flow Diagram:
                          +-------------------+   Выход 1 (команда вкл/выкл основной свет)
    

    [mqtt in] ---------> | Switch |---------------------------------------------+

    (слушаем кнопку) | (проверка payload) | |

    +-------------------+ Выход 2 (команда вкл/выкл торшер) |

    |---------------------------------------------+ |

    | Выход 3 (команда вкл/выкл подсветку) |

    +---------------------------------------------+ |

    v v

    [change] -> [mqtt out]

    Настройка ноды `Switch`:
  • Property: `msg.payload`
  • Правила:
  • * `is equal to` (string) `single` -> Выход 1

    * `is equal to` (string) `double` -> Выход 2

    * `is equal to` (string) `hold` -> Выход 3

  • Checking: `stopping after first match`
  • Пример обработки сообщения:
  • Кнопку нажали один раз. В Node-RED поступает сообщение:
  •     {

    "topic": "devices/buttons/living_room/action",

    "payload": "single",

    "_msgid": "..."

    }

  • Нода `Switch` проверяет `msg.payload`:
  • * Правило 1: `"single"` == `"single"`? Истина.

    * Сообщение отправляется в Выход 1.

    * Проверка прекращается.

  • Далее по потоку, соединенному с первым выходом, нода `Change` может сформировать команду для реле основного света, например, `{"command": "TOGGLE"}`.
  • Таким образом, одна нода `Switch` элегантно решает задачу логической маршрутизации, позволяя одному и тому же устройству ввода выполнять совершенно разные функции.

    ---

    Расширенная маршрутизация: другие свойства msg и типы данных

    Хотя проверка `msg.payload` является самым частым случаем, возможности ноды `Switch` гораздо шире. Вы можете строить условную логику, основываясь на любом свойстве объекта `msg` или даже на переменных контекста.

    > ⚠️ Внимание: При создании сложных систем избегайте проверок, завязанных на глубоко вложенные или сложные структуры в `msg`, если это можно решить на уровне `msg.topic`. Маршрутизация по топику обычно более производительна и делает поток более читаемым.

    Маршрутизация по `msg.topic`

    Это второй по популярности сценарий, особенно при работе с MQTT. Как мы знаем из предыдущих уроков, иерархия топиков — это мощный инструмент для организации данных. Нода `Switch` позволяет легко фильтровать и разделять потоки сообщений по темам.

    Пример: Контроллер получает данные от комбинированного датчика, который публикует температуру и влажность в разные топики:
    • `sensors/living_room/temperature`
    • `sensors/living_room/humidity`

    Нам нужно направить эти данные в разные логические ветки для обработки (например, для управления отоплением и увлажнителем).

    Настройка ноды `Switch`:
  • Property: `msg.topic`
  • Правила:
  • * `contains` (string) `temperature` -> Выход 1 (ветка обработки температуры)

    * `contains` (string) `humidity` -> Выход 2 (ветка обработки влажности)

  • Checking: `stopping after first match`
  • Теперь все сообщения, в топике которых есть слово `temperature`, пойдут на первый выход, а все с `humidity` — на второй. Это гораздо эффективнее, чем использовать две отдельные ноды `mqtt in` для каждого топика.

    Проверка типа данных (`is type`)

    Иногда важно не само значение, а его тип. Например, вы ожидаете от датчика число, а получаете строку из-за ошибки в прошивке устройства. Нода `Switch` может выступать в роли валидатора данных.

    Пример: Поток должен обрабатывать только числовые значения. Настройка ноды `Switch`:
  • Property: `msg.payload`
  • Правила:
  • * `is of type` `Number` -> Выход 1 (корректные данные, отправляем на обработку)

    * `otherwise` -> Выход 2 (некорректные данные, логируем ошибку)

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

    Использование `otherwise` (иначе)

    Выход `otherwise` — это эквивалент блока `else` в программировании. Он срабатывает, если ни одно из вышестоящих правил не оказалось истинным. Это крайне полезно для:

    • Обработки ошибок: Логировать или отправлять уведомления о непредусмотренных значениях.
    • Действий по умолчанию: Если сообщение не соответствует ни одному из специальных случаев, выполнить стандартное действие.

    Использование `otherwise` делает ваш поток более полным и предсказуемым, так как вы явно обрабатываете все возможные исходы.

    ---

    Продвинутые условия с помощью JSONata

    Когда стандартных операторов сравнения (`==`, `>`, `<` и т.д.) становится недостаточно, на помощь приходит JSONata. Это мощный язык запросов и преобразований для JSON-данных, который встроен во многие ноды Node-RED, включая `Switch`.

    Чтобы использовать JSONata в ноде `Switch`, нужно в выпадающем списке типа правила выбрать Expression (`J:`). Появится поле для ввода выражения, которое должно вернуть `true` (если условие выполнено) или `false` (если не выполнено).

    JSONata незаменима в следующих случаях:

    • Нужно проверить значение во вложенном объекте.
    • Нужно проверить несколько условий одновременно в одном правиле.
    • Нужно выполнить вычисления или преобразования перед сравнением.

    Пример 1: Проверка вложенного свойства

    Предположим, датчик присылает данные в сложном формате JSON:

    {
    

    "payload": {

    "device_id": "termo-kitchen-01",

    "data": {

    "temperature": 25.4,

    "battery": 98

    },

    "timestamp": 1678886400000

    }

    }

    Нам нужно включить кондиционер, если температура (`payload.data.temperature`) превышает 25 градусов. Стандартными средствами это сделать невозможно.

    JSONata-выражение:
    payload.data.temperature > 25
    

    Это выражение будет применено к объекту `msg` и вернет `true`, если температура действительно выше 25, направив сообщение в соответствующий выход.

    Пример 2: Комбинированная проверка

    А теперь усложним задачу. Свет в гостиной должен включаться, только если:

  • Сообщение пришло из топика, содержащего `living_room`.
  • `payload` содержит команду на включение (`"ON"`).
  • JSONata-выражение:
    $contains(topic, "living_room") and payload = "ON"
    

    Здесь мы используем:

    • `$contains(str, substr)`: встроенная функция JSONata для проверки наличия подстроки.
    • `and`: логический оператор "И".

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

    Пример 3: Проверка длины массива

    Иногда в `msg.payload` приходит массив данных (например, список активных устройств). Нам нужно выполнить действие, только если в списке есть хотя бы одно устройство.

    JSONata-выражение:
    $count(payload) > 0
    
    • `$count()`: функция для подсчета элементов в массиве.

    Это выражение вернет `true`, если массив в `msg.payload` не пустой.

    ---

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

    Давайте соберем все полученные знания в одном комплексном сценарии, который демонстрирует мощь ноды `Switch` как центрального элемента принятия решений.

    Сценарий: Свет в коридоре должен включаться автоматически при обнаружении движения, но только в ночное время. Днем свет включаться не должен, чтобы экономить электроэнергию.

    Для реализации нам понадобится переменная, хранящая текущее состояние "день/ночь". Будем использовать переменную контекста потока `flow.isNight`.

    Шаг 1: Установка переменной контекста

    Самый простой способ — использовать две ноды `Inject` для ручного переключения. В реальной системе это будет делать узел, отслеживающий время восхода/заката (например, `node-red-contrib-sun-position`).

    • Inject 1 (Ночь): Отправляет `true`, нода `Change` устанавливает `flow.isNight` в `true`.
    • Inject 2 (День): Отправляет `false`, нода `Change` устанавливает `flow.isNight` в `false`.
    Шаг 2: Обработка сигнала от датчика движения

    Датчик движения при срабатывании отправляет в MQTT сообщение с `payload`, равным `{"motion": true}`.

    Шаг 3: Построение логики с помощью `Switch`

    Когда от датчика приходит сообщение, нам нужно проверить два условия:

  • Это действительно сигнал о движении?
  • Сейчас ночь?
  • Для этого мы можем использовать две последовательные ноды `Switch`:

    [mqtt in]  (msg от датчика)
    

    |

    v

    +-----------------------------+

    | Switch 1: Проверка движения | (Правило: payload.motion is true)

    +-----------------------------+

    | (если ДА)

    v

    +----------------------------+

    | Switch 2: Проверка ночи | (Правило: flow.isNight is true)

    +----------------------------+

    | (если ДА)

    v

    [change] (формируем команду "ON") --> [mqtt out] (отправляем на реле света)

    Настройка `Switch 1`:
    • Property: `msg.payload.motion` (используем JSON-путь)
    • Правило: `is true` -> Выход 1
    • Все сообщения без `{ "motion": true }` будут отфильтрованы.
    Настройка `Switch 2`:
    • Property: `flow.isNight` (выбираем `flow` в выпадающем списке)
    • Правило: `is true` -> Выход 1

    Такая последовательная фильтрация очень наглядна и легко отлаживается. Сообщение дойдет до реле света, только если пройдет через "ворота" обоих `Switch`-узлов.

    Этот пример наглядно демонстрирует, как с помощью ноды `Switch` строятся нетривиальные сценарии автоматизации, которые учитывают множество факторов (событие от датчика, внешнее состояние) для принятия верного решения.

    Что дальше

    В этом уроке мы досконально изучили ноду `Switch` — от базовой маршрутизации по `payload` до сложных условий с JSONata и контекстом. Теперь вы можете создавать потоки с ветвлениями и сложной логикой. В следующем уроке мы рассмотрим ноду `Function`, которая открывает безграничные возможности для обработки данных и реализации любой, даже самой сложной логики с помощью JavaScript.