Руководство по созданию драйверов для Dynamic Driver
Введение
Dynamic Driver позволяет создавать собственные драйверы для управления различными устройствами.
В этом руководстве мы рассмотрим процесс создания драйвера с нуля, шаг за шагом, и расскажем о лучших практиках разработки.
Редактор драйверов (UI)
Редактор драйверов служит для создания или редактирования драйверов, сохранения проекта и загрузки проекта со всеми драйверами в проекте.

Редактор драйверов находится в левой панели U-Logic

Редактор состоит из трёх зон:
Верхняя панель, в которой есть селектор режима работы редактора Загрузить из списка или Создать новый.

Центральная область — редактор драйвера
Нижняя панель:
Скачать проект - Сохраняет проект и все драйвера на ваш компьютер.
Загрузить проект - Загружает проект, с вашего компьютера.
Сохранить - Сохраняет изменения драйвера.
Удалить - Удаляет драйвер который открыт в редакторе.

Как добавить драйвер
В редакторе драйверов выберите режим «Создать новый»
Укажите имя (файла) драйвера.
Отредактируйте шаблон драйвера.
Нажмите «Сохранить».

Как отредактировать драйвер
В редакторе драйверов выберите режим «Загрузить из списка»
Выберите драйвер из списка драйверов
Внесите изменения на нужных вкладках
Нажмите «Сохранить»
Нажмите «Деплой», чтобы изменения стали доступны узлам

Как подгружается драйвер и где он виден
После добавления/обновления драйвера через интерфейс редактора драйверов нажмите «Сохранить».
Список доступных драйверов обновляется после сохранения. Драйвер становится виден в настройках узла
Device Commandв поле «Driver».

Узлы и их назначение
В Dynamic-Driver используется 4 ключевых узла для работы с устройством через драйвер:
Device Connection— отвечает за включение драйвера. Device Connection является конфигурационным узлом и не дсотупен в палитре узловTransport— отвечает за сетевое подключение к устройству. Transport является конфигурационным узлом и не дсотупен в палитре узловDevice Command— отправляет команды, определённые драйверомDevice Response Listener— принимает и маршрутизирует ответы, распознанные драйвером
Узел: Device Connection
Назначение: Подключает драйвер и связывает драйвер с Трнаспортом (TCP/UDP/HTTP и др.).
Основные настройки:
Имя конфигурационного узла.
Драйвер. Здесь необходимо выбрать нужный драйвер
Транспорт (Transport): TCP | UDP | HTTP/HTTPS | TELNET | SSH
(TCP/UDP/HTTP…)-Config. здесь необходимо создать или выбрать существующий транспорт.
Keep‑Alive — включает периодический вызов метода драйвера
KeepAlive().Интервал Keep‑Alive — период (секунды) между вызовами
KeepAlive(). Поле доступно только при включённом Keep‑Alive.

Узел: Transport(TCP-Config)
Назначение: Выполняет подключение к устройству передает команды из драйвера и отправляет драйверу полученные от устройства данные.
Основные настройки:
Название конфигурационного узла.
Хост. IP адрес устройства
Порт устройства
Переподключение. Таймаут между попытками подключения к устройству
Возвращать:
Байты(Buffer), для устройств которые работают в HEX формате
Строка, для устройств которые работаю в формате ASCII
Автоподключение. Транспорт будет пытаться автоматически подключится, при каждом обрыве соединения и при старте программы. По умолчанию включенно.
Debug. Включает отладочные сообщения транспорта.

Узел: Transport(UDP-Config)
Назначение: Выполняет подключение к устройству передает команды из драйвера и отправляет драйверу полученные от устройства данные.
Основные настройки:
Название конфигурационного узла.
Внешний порт. Порт устройства
Локальный порт. Порт который контроллер будет слушать.
Тип соединения. UNICAST, BROADCAST, MULTICAST,
Формат данных:
Строка, для устройств которые работаю в формате ASCII
Base64
HEX
Автоподключение. Транспорт будет пытаться автоматически подключится, при каждом обрыве соединения и при старте программы. По умолчанию включенно.
Debug. Включает отладочные сообщения транспорта.

Узел: Transport(HTTP-Config)
Назначение: Выполняет HTTP(S)-подключение к устройству/шлюзу API, отправляет запросы согласно командам драйвера и передаёт ответы драйверу.
Основные настройки:
Название конфигурационного узла (Имя)
Сервер (URL): базовый адрес, например
http://192.168.1.1(поддерживает подстановки изenv,flow,global,msg)Метод:
GET,POST,PUT,DELETE,HEAD,Установить через(взять из сообщения)Полезная нагрузка (для GET):
Игнорировать|Добавить в запрос|Добавить в телоИспользовать TLS: выбор конфигурации TLS; параметр «Проверять SSL сертификаты» (самоподписанные ⇄ разрешить)
Аутентификация:
Базовая|Digest|Bearer; поля Имя пользователя/Пароль или Токен BearerСохранять соединение (keep‑alive)
Отправлять ошибки (передавать ошибки далее по потоку)
Небезопасный HTTP парсер (разрешает нестандартные ответы)
Следовать переадресациям (HTTP 301/302)
Тайм‑аут запроса, мс (переопределяет глобальный)
Возвращать:
UTF‑8|Бинарные данные|JSONЗаголовки: настраиваемый список с типами значений (
other,msg.и пресеты)Поля URL/логин/пароль поддерживают типизированный ввод (строка,
msg.,flow.,global.,env.)
Рекомендации:
Указывайте протокол в URL (
http://илиhttps://)Для самоподписанных сертификатов либо подключайте корректную TLS‑конфигурацию, либо временно отключайте проверку
Для JSON‑API установите «Возвращать: JSON», чтобы автоматически получить объект
Переопределение параметров из драйвера/сообщения:
url— полный URL (перекрываетserver+path)path— относительный путь, добавляется кservermethod— HTTP‑метод (GET/POST/…)headers— объект заголовков запросаcookies— объект кукиpayload— тело запроса (строка/Buffer/объект)requestTimeout— тайм‑аут, мсfollowRedirects—true/falserejectUnauthorized—true/false(TLS)Для
GET: если в конфиге выбран режим «Полезная нагрузка», объектpayloadдобавится в query или тело

Узел: Transport(Telnet-Config)
Назначение: устанавливает Telnet‑сеанс с устройством, выполняет вход по логину/паролю и передаёт команды/ответы между драйвером и устройством.
Основные настройки:
Имя — отображаемое имя конфигурационного узла
Хост — IP/домен Telnet‑сервера (typed input:
str,msg,flow,global,env)Порт — обычно
23(typed input:num,str,msg,flow,global,env)Логин — имя пользователя (typed input)
Пароль — пароль (typed input)
Выражение для логина — regex для ввода логина, по умолчанию
[^\S\s]*login[: ]*или[^\S\s]*login[: ]*Выражение для пароля — regex для ввода пароля, по умолчанию
[^\S\s]*password[: ]*Выражение для shell prompt — regex окончания вывода, по умолчанию
[^\S\s]*[$#>]Переподключение — автоматически восстанавливать соединение при разрыве (флаг)
Интервал (мс) — задержка между попытками переподключения (по умолчанию 5000)
Авто‑подключение — подключаться при старте (флаг)
Debug — выводить отладочные сообщения (флаг)
Поля «Хост», «Порт», «Логин», «Пароль» поддерживают источники
msg/flow/global/envчерез typed inputПоля «Выражение для логина/пароля/shell» вводятся как
regex‑шаблоныИмя узла формируется как
telnet@<host>:<port>, если не задано «Имя»
Переопределение из драйвера/контекста:
При выборе соответствующего источника значения «Хост», «Порт», «Логин», «Пароль» могут считываться из
msg.*,flow.*,global.*илиenv.*Флаги «Переподключение», «Авто‑подключение», «Debug» задаются в конфигурации узла
Примечание
Тщательно подберите регулярные выражения для авторизации — от них зависит корректность входа и парсинга вывода

Узел: Transport(SSH-Config)
Назначение: устанавливает защищённый SSH‑сеанс с устройством/хостом и передаёт команды/ответы между драйвером и устройством через интерактивный shell. Поддерживает аутентификацию по паролю или по приватному ключу.
Основные настройки:
Имя — отображаемое имя конфигурационного узла
Хост — IP/домен SSH‑сервера (typed input:
str,msg,flow,global,env)Порт — обычно
22(typed input:num,msg,flow,global,env)Логин — имя пользователя (typed input)
Пароль — пароль пользователя (typed input; не используется, если указан ключ)
Ключ — путь к приватному ключу (если указан, используется вместо пароля)
Переподключение — автоматически восстанавливать соединение при разрыве (флаг)
Интервал (мс) — задержка между попытками переподключения (по умолчанию 5000)
Авто‑подключение — подключаться при старте (флаг)
Debug — выводить отладочные сообщения (флаг)
Переопределение из драйвера/контекста:
При выборе соответствующего источника значения «Хост», «Порт», «Логин», «Пароль» могут считываться из
msg.*,flow.*,global.*илиenv.*Флаги «Переподключение», «Авто‑подключение», «Debug» задаются в конфигурации узла
Путь к ключу задаётся в конфигурации; передача через
msgне рекомендуется по соображениям безопасности

Статусы узла:
Серый — отключён, Красный — ошибка, Зелёный — подключено
Текстовый статус под узлом: Connecting… / Connected / Disconnected / Error
Советы по настройке:
Для TCP‑клиента: укажите IP/порт устройства, проверьте доступность порта брандмауэром
Для Serial: проверьте параметры порта (baud rate, parity, data/stop bits)
Для нестабильных линий включите реконнект и увеличьте тайм‑ауты
Рисунок: Форма настроек узла Device Connection — поля Transport, Host/IP, Port, Encoding, Keep Alive
Узел: Device Command
Назначение: отправка команд, описанных в драйвере, с заполнением параметров из UI или из сообщения.
Ключевые поля:
Driver — выбор драйвера (из метаданных драйвера)
Command — список команд, определённых драйвером (динамически меняется при выборе Driver)
Parameters — набор полей для ввода значений (тип/обязательность/диапазоны/enum — берутся из описания команды)
Источник параметров: из UI или из входного сообщения (
msg.parameters,msg.payload)Ожидать ответ — если включено, узел будет ждать распознанный драйвером ответ
Timeout — время ожидания ответа (если включено «Ожидать ответ»)
Повторы/Троттлинг (если доступны) — ограничение частоты отправки, политика повторов
Сообщения:
Вход:
msg.parameters(объект параметров),msg.command(имя команды),msg.connection(bool — подключить/отключить), и/илиmsg.payload(сквозной)Выход 1: статус соединения (
connected|disconnected|error) и события транспортаВыход 2: сквозной
msg.payloadбез изменений
Типовые сценарии:
Заполнение из UI — удобно для статических команд
Динамика из
msg.parameters— удобно при вычислении значений в рантайме
Переопределения через сообщение:
msg.command— переопределяет выбранную в узле командуmsg.parameters— дополняет/замещает параметры команды из узла (приоритет уmsg)msg.connection—trueподключиться |falseотключиться (в этом случае команда не отправляется)
Валидация параметров (по метаданным драйвера):
Обязательность: параметры с
required: trueне могут быть пустымиТипы:
number(приведение + проверкаmin/max),boolean(строки"true"/"1"→ true),enum(значение должно входить в список)Невалидные значения игнорируются и заменяются сохранёнными в узле (если есть)
Статусы узла:
Серый «IDLE» — узел создан, ожидание действий
Жёлтый «Подключение…» — установление соединения
Зелёный «Подключено» — соединение установлено
Красный «Отключено» — соединение разорвано
«Статус: <код> - <текст>» — транспортный ответ (HTTP‑коды и т.п.)
Красный «Не настроено соединение» — не выбран
device-connectionв настройках узлаКрасный «Ошибка: …» — ошибка формирования/отправки команды

Узел: Device Response Listener
Назначение: приём и маршрутизация ответов, которые распознал драйвер согласно настроенным в нём шаблонам.
Ключевые настройки/поведение:
Соответствие выходов:
type→ номер выходаВыход по умолчанию: ответы без сопоставления типа направляются на 1‑й выход
Структура выходного сообщения:
msg.payload— распарсенный объект ответаmsg.type— тип ответа (например,status,error,volumeStatusи т.п.)msg.originalMsg— дополнительная информация/сырой пакет (если публикуется драйвером)
Настройки:
Соединение — тот же
device-connection, что используетDevice CommandВыходы — количество физических выходов узла (1–20)
Типы ответов — список типов из драйвера с назначением на выход:
галочка «включить тип» и выпадающий «номер выхода» (0..N‑1)
счётчики: «Выбрано ответов», «Настроено выходов»/«из N»
предупреждение о конфликтах, когда несколько типов направлены на один и тот же выход
Вход узла:
Поддерживается управление соединением через
msg.connection(trueподключиться /falseотключиться)
Маршрутизация:
Используется
payload.type. Если тип назначен в карте — сообщение уходит на соответствующий выходЕсли тип не назначен — сообщение отправляется на 1‑й выход (индекс 0)
На событие формируется массив из N элементов, где только один выбранный выход получает
msg, остальные —null
Статусы узла:
Серый «IDLE» при инициализации
Зелёный «Подключено» — при событии подключения
Красный «Отключено» — при событии отключения
Красный «Ошибка» — при ошибке; текст берётся из события
Пример выходного сообщения:
{
"payload": {
"type": "MatrixTie",
"input": 1,
"output": 2
},
}

Схема зависимости узлов Dynamic-Driver
sequenceDiagram
participant DCMD as Device Command
participant DCON as device-connection
participant TR as Transport
DCMD->>DCON: Создание Конфигурации Драйвер + Транспорт
Note over DCMD: Подключение Драйвера и Транспорта
Note over DCON: Выбор драйвера и установка транспорта
DCON->>TR: Создание транспорта и настройка транспорта (TCP/UDP/HTTP/Telnet/SSH)
Note over TR: Настройка транспорта
DCON->>DCMD: Создание Конфигурации Драйвер + Транспорт
Схема работы узлов Dynamic-Driver
sequenceDiagram
participant UI as Сообщение/триггер
participant DCMD as Device Command
participant DCON as device-connection (экземпляр Драйвера)
participant TR as Transport
participant DEV as Устройство
participant RLIST as Response Listener
UI->>DCMD: msg.command / msg.parameters / msg.connection
DCMD->>DCON: Команда + параметры
Note over DCON: Внутри: driver.format(command, params)
DCON->>TR: payload (TCP/UDP/HTTP/Telnet/SSH)
TR->>DEV: Запрос
DEV-->>TR: Ответ
TR-->>DCON: Сырые данные
Note over DCON: Внутри: driver.parse(raw) → { type, ... }
DCON-->>RLIST: Распознанный ответ по type
DCON-->>DCMD: Статусы подключения / коды
Шаги настройки (матрица действий)
Шаг |
Действие |
Где |
Результат |
|---|---|---|---|
1 |
Создайте/отредактируйте драйвер и сохраните |
Редактор драйверов |
Драйвер доступен для выбора |
2 |
Добавьте |
Палитра узлов |
Открыт |
3 |
Внутри |
Внутри Device Command или Response Listener |
|
4 |
Внутри |
device-connection |
Транспорт создан и привязан; |
5 |
Настройте |
Палитра узлов |
Отправка команд |
6 |
Добавьте |
Палитра узлов |
Маршрутизация ответов по типам |
7 |
Деплой и тест |
Кнопка Deploy |
Команды/ответы идут по потоку |
Подготовка к разработке
Необходимые инструменты
Базовые знания JavaScript и Node.js
Документация на протокол взаимодействия с вашим устройством
Структура драйвера
Драйвер представляет собой модуль который включает в себя модуль BaseDriver. BaseDriver предоставляет необходимую функциональность для взаимодействия с системой и устройством.
Основные элементы драйвера
Метаданные - описательная информация о драйвере
Команды - доступные для выполнения команды и их параметры
Обработчики ответов - шаблоны для распознавания и обработки ответов устройства
Методы форматирования команд - преобразование высокоуровневых команд в формат устройства
Метод инициализации - настройка драйвера при подключении
Пошаговая разработка драйвера
Шаг 1: Создание файла драйвера
Шаг 2: Определение базовой структуры драйвера
const BaseDriver = require('base-driver');
/**
* Драйвер для устройства MyDevice
*/
class MyDeviceDriver extends BaseDriver {
}
module.exports = MyDeviceDriver;
Шаг 3: Добавление метаданных драйвера
Метаданные помогают системе идентифицировать драйвер и отображать информацию о нем в интерфейсе. Добавьте следующий статический объект в класс драйвера:
static metadata = {
name: 'MyDevice', // Имя драйвера
manufacturer: 'MyCompany', // Производитель устройства
version: '1.0.0', // Версия драйвера
description: 'Драйвер для устройства MyDevice от MyCompany' // Описание
};
Шаг 4: Определение команд
Команды определяют действия, которые можно выполнить с устройством. Каждая команда описывается в объекте static commands.
Для каждого параметра можно указать:
name— имя поля.type—string|number|boolean.description— пояснение.required— еслиtrue, параметр обязателен. Узел «Device Command» проверит его наличие и подсветит поле в редакторе.Дополнительно:
min/max(числа),enum(список допустимых значений).
static commands = {
setPower: {
description: 'Включение/выключение устройства',
parameters: [
{
name: 'value',
type: 'boolean',
description: 'Состояние питания (true=включено, false=выключено)',
required: true
}
]
},
setVolume: {
description: 'Установка громкости',
parameters: [
{
name: 'level',
type: 'number',
description: 'Уровень громкости (0-100)',
required: true,
min: 0,
max: 100
}
]
},
setInput: {
description: 'Выбор входного источника',
parameters: [
{
name: 'source',
type: 'string',
description: 'Имя источника (HDMI1, HDMI2, USB и т.д.)',
required: true,
enum: ['HDMI1', 'HDMI2', 'USB', 'Component', 'Composite']
}
]
},
getStatus: {
description: 'Запрос статуса устройства',
parameters: []
}
};
Что такое static commands и зачем они нужны (простыми словами)
Это «каталог возможностей» вашего драйвера. Здесь вы перечисляете, какие действия устройство умеет выполнять, и какие данные нужны для каждого действия.
По этому описанию система автоматически строит узел
Device Command.Каждая команда — это объект с описанием и списком параметров. Каждый параметр задаёт тип (string, number, bool), обязательность (
required:true) и ограничения (min/max).
Где это отображается в интерфейсе
В узле
Device Command:поле «Команда» — выпадающий список из ключей ваших команд (
setPower,setVolume, …);блок «Параметры команды» — динамически сгенерированные поля:
number→ числовое поле cmin/max;boolean→ выборTrue/False;enum→ выпадающий список значений;string→ текстовое поле;
над параметрами показываются «Описание».
Как это работает во время выполнения
Значения, введённые в UI, сохраняются как дефолты в конфигурации узла.
В потоке можно переопределять:
msg.parameters— подставить значения параметров на лету (приоритет у входного сообщения);
Невалидные значения игнорируются или заменяются сохранёнными дефолтами в узле.
Мини‑чеклист
В
static commandsопишите команды и параметры (типы/ограничения/required).Реализуйте методы с теми же именами (см. «Шаг 6: Реализация методов команд»).
Нажмите Сохранить/Деплой драйвера.
Откройте узел
Device Command: выберите соединение → драйвер → команду, заполните параметры.При необходимости переопределяйте
msg.commandиmsg.parametersв потоке.
Результат описания в static commands команды setPower
Шаг 5: Определение обработчиков ответов
Обработчики ответов определяют, как драйвер будет интерпретировать данные, полученные от устройства. Используйте регулярные выражения для поиска шаблонов в ответах.
static responses = {
status: {
description: 'Статус устройства',
matcher: {
pattern: /Status: (.+), Power: (.+), Volume: (.+), Input: (.+)/
},
extract: function(match) {
return {
status: match[1],
power: match[2] === 'ON',
volume: parseInt(match[3], 10),
input: match[4]
};
}
},
powerStatus: {
description: 'Статус питания',
matcher: {
pattern: /Power: (.+)/
},
extract: function(match) {
return {
power: match[1] === 'ON'
};
}
},
volumeStatus: {
description: 'Текущая громкость',
matcher: {
pattern: /Volume: (\d+)/
},
extract: function(match) {
return {
volume: parseInt(match[1], 10)
};
}
},
inputStatus: {
description: 'Текущий вход',
matcher: {
pattern: /Input: (.+)/
},
extract: function(match) {
return {
input: match[1]
};
}
},
error: {
description: 'Ошибка устройства',
matcher: {
pattern: /Error: (.+)/
},
extract: function(match) {
return {
message: match[1]
};
}
}
};
Что такое responses (простыми словами)
Это словарь правил распознавания ответов в драйвере: каждый ключ — имя типа (
status,error,volumeStatus…), внутри — как распознать (matcher) и что извлечь (extract).Когда устройство присылает сырые данные, драйвер сопоставляет их с
matcher, передает вextractи возвращает объект с полемtypeи параметрами.type- помогает узлу Response Listener определить на какой выход отправить результат.
Где это видно в интерфейсе
В узле
Device Response Listenerв секции «Типы ответов»: чекбокс «включить» и выпадающий «Номер выхода».Счётчики: «Выбрано ответов» и «Настроено выходов / из N».
Предупреждение о конфликтах, если несколько типов назначены на один и тот же выход.
Пошагово: что происходит во время выполнения
matcher и extract
Устройство прислало сырой текст:
Status: READY, Power: ON, Volume: 50, Input: HDMI1
В драйвере в
static responsesнастроен типstatus:
static responses = {
status: {
description: 'Статус устройства',
matcher: { pattern: /Status: (.+), Power: (.+), Volume: (\d+), Input: (.+)/ },
extract: function(match) {
return {
type: 'status', // ключ для маршрутизации
status: match[1],
power: match[2] === 'ON',
volume: parseInt(match[3], 10),
input: match[4]
};
}
}
};
Что происходит:
matcher находит совпадение по регулярному выражению и формирует массив
match;extract извлекает значения из
matchи возвращает объект с полемtype: 'status'и параметрами;Device Response Listenerчитаетpayload.type === 'status'и отправляет сообщение на назначенный дляstatusвыход.
Драйвер публикует сообщение вида:
{ "payload": { "type": "status", "status": "READY", "power": true, "volume": 50, "input": "HDMI1" } }
Response Listener берёт
payload.type, находит назначенный выход и отправляет сообщение только туда;Если тип не включён/не назначен — сообщение уходит на 1‑й выход.
Мини‑чеклист
Выберите соединение (тот же
device-connection, что уDevice Command).Задайте «Выходы» (1–20).
Включите нужные типы и назначьте им выход.
Убедитесь, что нет конфликтов (или скорректируйте назначения).
Деплой и подключите узел debug к соответствующим выходам.
Настройка типов ответов и назначение выходов в Response Listener
Шаг 6: Реализация методов команд
Для каждой команды создайте метод с тем же именем, который возвращает объект с полем payload содержащим команду в формате, понятном устройству.
// Метод для команды setPower
setPower(params) {
const { value } = params;
// Обновляем состояние
this.state.power = value;
// Возвращаем объект с полем payload - форматированной командой
return { payload: `PWR ${value ? 'ON' : 'OFF'}\r\n` };
}
// Метод для команды setVolume
setVolume(params) {
const { level } = params;
// Проверка диапазона
const safeLevel = Math.max(0, Math.min(100, level));
// Обновляем состояние
this.state.volume = safeLevel;
// Возвращаем объект с полем payload
return { payload: `VOL ${safeLevel}\r\n` };
}
// Метод для команды setInput
setInput(params) {
const { source } = params;
// Обновляем состояние
this.state.input = source;
// Возвращаем объект с полем payload
return { payload: `INPUT ${source}\r\n` };
}
// Метод для команды getStatus
getStatus() {
// Команда без параметров
return { payload: `STATUS\r\n` };
}
Шаг 7: Реализация метода инициализации
Метод initialize вызывается автоматически после успешного подключения к устройству. Используйте его для запроса начального состояния устройства или выполнения других действий при подключении.
initialize() {
// Запрос начального статуса устройства
this.sendCommand('getStatus');
// Можно добавить дополнительные обработчики ответов динамически
this.addResponseHandler(/Version: (.+)/, (match, data) => {
// Обновляем информацию об устройстве
this.deviceInfo.firmwareVersion = match[1];
// Возвращаем обработанные данные
return {
type: 'version',
version: match[1]
};
});
this.addResponseHandler(/Serial: (.+)/, (match, data) => {
this.deviceInfo.serialNumber = match[1];
return {
type: 'serial',
serialNumber: match[1]
};
});
}
Шаг 8: Сборка полного драйвера
Объединим все части для создания полного драйвера:
const BaseDriver = require('base-driver');
/**
* Драйвер для устройства MyDevice
*/
class MyDeviceDriver extends BaseDriver {
// Метаданные драйвера
static metadata = {
name: 'MyDevice',
manufacturer: 'MyCompany',
version: '1.0.0',
description: 'Драйвер для устройства MyDevice от MyCompany'
};
// Определение команд
static commands = {
setPower: {
description: 'Включение/выключение устройства',
parameters: [
{
name: 'value',
type: 'boolean',
description: 'Состояние питания (true=включено, false=выключено)',
required: true
}
]
},
setVolume: {
description: 'Установка громкости',
parameters: [
{
name: 'level',
type: 'number',
description: 'Уровень громкости (0-100)',
required: true,
min: 0,
max: 100
}
]
},
setInput: {
description: 'Выбор входного источника',
parameters: [
{
name: 'source',
type: 'string',
description: 'Имя источника (HDMI1, HDMI2, USB и т.д.)',
required: true,
enum: ['HDMI1', 'HDMI2', 'USB', 'Component', 'Composite']
}
]
},
getStatus: {
description: 'Запрос статуса устройства',
parameters: []
}
};
// Определение обработчиков ответов
static responses = {
status: {
description: 'Статус устройства',
matcher: {
pattern: /Status: (.+), Power: (.+), Volume: (.+), Input: (.+)/
},
extract: function(match) {
return {
status: match[1],
power: match[2] === 'ON',
volume: parseInt(match[3], 10),
input: match[4]
};
}
},
powerStatus: {
description: 'Статус питания',
matcher: {
pattern: /Power: (.+)/
},
extract: function(match) {
return {
power: match[1] === 'ON'
};
}
},
volumeStatus: {
description: 'Текущая громкость',
matcher: {
pattern: /Volume: (\d+)/
},
extract: function(match) {
return {
volume: parseInt(match[1], 10)
};
}
},
inputStatus: {
description: 'Текущий вход',
matcher: {
pattern: /Input: (.+)/
},
extract: function(match) {
return {
input: match[1]
};
}
},
error: {
description: 'Ошибка устройства',
matcher: {
pattern: /Error: (.+)/
},
extract: function(match) {
return {
message: match[1]
};
}
}
};
// Инициализация при подключении
initialize() {
console.log('Инициализация устройства MyDevice');
// Запрос начального статуса при подключении
this.publishCommand('getStatus');
}
// Методы команд
setPower(params) {
if (this.debug) {
console.log('Выполнение команды setPower:', params);
}
const { value } = params;
this.state.power = value;
return { payload: `PWR ${value ? 'ON' : 'OFF'}\r\n` };
}
setVolume(params) {
if (this.debug) {
console.log('Выполнение команды setVolume:', params);
}
const { level } = params;
// Проверка диапазона
const safeLevel = Math.max(0, Math.min(100, level));
this.state.volume = safeLevel;
return { payload: `VOL ${safeLevel}\r\n` };
}
setInput(params) {
if (this.debug) {
console.log('Выполнение команды setInput:', params);
}
const { source } = params;
this.state.input = source;
return { payload: `INPUT ${source}\r\n` };
}
getStatus() {
if (this.debug) {
console.log('Выполнение команды getStatus');
}
return { payload: `STATUS\r\n` };
}
// Обработка нестандартных ответов
parseResponse(data) {
try {
// Примеры обработки нестандартных ответов
if (data.includes('System Info:')) {
const info = data.replace('System Info:', '').trim();
return {
type: 'systemInfo',
info: info
};
}
return null; // Возвращаем null если ответ не распознан
} catch (error) {
console.error('Ошибка при обработке ответа:', error);
return {
type: 'error',
message: error.message,
raw: data
};
}
}
}
module.exports = MyDeviceDriver;
Использование драйвера
После создания драйвера, нажмите кнопку сохранить
Добавьте узел «Device Command» в рабочую область
В настройках узла выберите драйвер из списка
Настройте параметры подключения (тип транспорта, адрес, порт)
В настройках узла «Device Command» выберите команду для выполнения
Добавьте узел «Device Response Listener» для получения ответов от устройства
Запустите поток и проверьте работу драйвера
Проверка регулярных выражений
Если обработчики ответов не срабатывают:
проверьте корректность регулярных выражений:
// Тестирование регулярного выражения const regex = /Status: (.+), Power: (.+), Volume: (.+)/; const testData = "Status: READY, Power: ON, Volume: 50"; const match = testData.match(regex); if (match) { console.log('Совпадение найдено:'); console.log('Статус:', match[1]); console.log('Питание:', match[2]); console.log('Громкость:', match[3]); } else { console.log('Совпадение не найдено'); }
проверьте настройки формата данных в Транспорте (String/Buffer(HEX))
Проблемы с форматированием команд
Если команды не отправляются корректно, убедитесь, что:
Метод (функция) имеет то же имя, что и команда в
static commandsКоманда корректно возвращает объект с полем
payload
Расширенные возможности
Обработка сложных протоколов
Некоторые устройства используют сложные протоколы с двоичными данными или специальными форматами. В таких случаях можно использовать дополнительные методы обработки данных:
// Для бинарных данных можно использовать буферы
binaryCommand(params) {
const { data } = params;
// Создаем буфер с командой
const buffer = Buffer.from([0x02, data.charCodeAt(0), 0x03]);
return { payload: buffer };
}
// Обработка бинарных ответов
static responses = {
binaryResponse: {
description: 'Бинарный ответ',
matcher: {
pattern: /\x02(.+)\x03/
},
extract: function(match) {
// Преобразование бинарных данных
const payload = match[1];
const value = payload.charCodeAt(0);
return {
value: value
};
}
}
};
Множественные ответы и publishResponse
Иногда устройство присылает несколько независимых сообщений в одном сетевом пакете. Базовый класс драйвера предоставляет два вспомогательных метода, благодаря которым такая ситуация легко обрабатывается.
Когда использовать:
Один ответ – верните объект из
parseResponse()с помощьюreturn.Несколько ответов сразу – верните
Arrayобъектов, каждый будет опубликован системой по очереди.Асинхронная логика – внутри
parseResponse()вызывайтеthis.publishResponse()столько раз, сколько нужно, и затем вернитеreturn null.
// Пример: в буфере сразу два ответа разделены переводом строки
parseResponse(data) {
const parts = data.data.split('\r\n').filter(Boolean);
if (parts.length === 1) {
// Простой случай – один ответ
return this._parseSingle(parts[0]);
}
// Сложный случай: публикуем через publishResponse
parts.forEach(part => {
const parsed = this._parseSingle(part);
// second parameter не обязателен, но полезен для дебага, в данном случае в raw записываем сырой ответ
this.publishResponse(parsed, { raw: part });
});
return null;
}
publishResponse(payload, originalMsg)– публикует один распарсенный ответ наружу.payload– объект-результат вашего парсинга (то же, что обычно возвращаетparseResponse).originalMsg– необязательный объект-обёртка для сырого пакета/любой вспомогательной информации. Он попадёт в узел «Device Response Listener» какmsg.originalMsg. Если не нужен — опустите параметр.
Важно: если вернёте
nullилиundefined, система считает, что драйвер уже опубликовал ответы. Если ответ передается не корректно, он всегда попадает на первый выход.
Важно: если драйвер возвращает сообщение, которое не соответствует правилам маршрутизации (например, отсутствует поле type или его значение не указано в outputsMap), узел-слушатель направит такое сообщение на первый выход. В этом случае настроенная карта выходов игнорируется.
Инициирование команд и publishCommand
Иногда драйверу нужно самому отправить команду устройству — например, при initialize() или при изменении внутренних таймеров.В BaseDriver имеется вспомогательный метод publishCommand:
publishCommand(command, params = {})
command- Имя командыparams- Параметры команды
Когда использовать
Запрос начальных статусов в
initialize().Реакция на внутренние события драйвера без участия внешних узлов.
Пример: запрос статуса при инициализации
initialize() {
// Вместо прямого вызова sendCommand → write используйте publishCommand
this.publishCommand('getStatus');
// Или несколько команд сразу
this.publishCommand('getVolume');
this.publishCommand('getInput');
}
Передача готового объекта
Если у вас уже есть сформированный объект (например, после вычислений), можно передать его напрямую:
this.publishCommand({ command: 'setPower', parameters: { value: true } });
Примеры вызова publishCommand
// 1) Булев параметр – включаем питание
this.publishCommand('setPower', { value: true });
// 2) То же, но сразу объектом
this.publishCommand({ command: 'setPower', parameters: { value: false } });
// 3) Числовой параметр – установить громкость
this.publishCommand('setVolume', { level: 25 });
// 4) Значение из enum – переключиться на HDMI2
this.publishCommand('setInput', { source: 'HDMI2' });
// 5) Несколько числовых параметров
this.publishCommand('multiSet', { brightness: 80, contrast: 60 });
Функция Keep‑Alive
Драйвер умеет периодически вызывать метод KeepAlive() вашего драйвера. Это удобно для поддержания соединения и регулярного опроса статусов.
Параметры в настройках узла:
Keep Alive — включает/выключает периодические вызовы (по умолчанию выключено).
Interval — интервал в секундах между вызовами
KeepAlive().
Как это работает:
При включённом флаге узел запускает таймер с указанным интервалом.
По таймеру вызывается метод
KeepAlive()текущего драйвера.Внутри
KeepAlive()вы формируете одну или несколько команд черезpublishCommand(...).

Пример реализации в драйвере:
KeepAlive() {
// Запросить актуальные статусы устройства
this.publishCommand('getStatus');
// Дополнительно — опросить громкость
this.publishCommand('getVolume');
}
Подробно о параметрах команд
Ниже приведены типовые сценарии и то, как они описываются в массиве parameters.
Сценарий |
Как описать в |
Что увидит пользователь в узле Device Command |
|---|---|---|
Несколько числовых параметров |
|
Два numeric-input поля. |
Выпадающий список (enum) |
|
Dropdown-меню со значениями из |
Число с пределами |
|
Numeric-input с валидацией |
Булев параметр |
|
Dropdown-меню |
Примеры описаний
// Несколько параметров
multiSet: {
description: 'Яркость и контраст',
parameters: [
{ name: 'brightness', type: 'number', min: 0, max: 100, required: true },
{ name: 'contrast', type: 'number', min: 0, max: 100, required: true }
]
}
// Выпадающий список
setInput: {
parameters: [
{ name: 'source', type: 'string', enum: ['HDMI1', 'HDMI2', 'USB'] }
]
}
// Число с пределами
setVolume: {
parameters: [
{ name: 'level', type: 'number', min: 0, max: 100 }
]
}
// Булево
setPower: {
parameters: [
{ name: 'value', type: 'boolean', required: true }
]
}
Если у параметра указано
required: true, узел не позволит оставить поле пустым.
Динамическое заполнение параметров из сообщения
Пример:
// Function-node перед Device Command
msg.parameters = { brightness: 80, contrast: 55 };
return msg;
Если параметр содержит enum, убедитесь, что переданное значение входит в список.