Skip to content

Repository files navigation

layoutarticle
titlePlugin API
date2025-11-19 12:00:00 +0300
categoriesbary install
descriptionПолное руководство по созданию плагинов для BARY с продвинутыми возможностями

Plugin API

Введение

Plugin API позволяет разработчикам создавать собственные плагины (драйверы) для интеграции новых устройств с системой BARY. Плагины работают как отдельные процессы и взаимодействуют с основным приложением через IPC (Inter-Process Communication).

Возможности:

  • Поддержка любых IoT устройств и протоколов
  • Изоляция драйверов в отдельных процессах
  • Автоматическая установка зависимостей
  • Логирование и мониторинг
  • Публикация событий в реальном времени
  • Mixins — модульное расширение функциональности
  • Templates — конфигурационные файлы устройств
  • Динамические capabilities — генерация интерфейса в runtime
  • Settings_ex — динамические настройки с фильтрацией

Установка и структура проекта

Базовая структура плагина

my-plugin/
├── package.json # Зависимости и метаданные
├── tsconfig.json # Конфигурация TypeScript
├── webpack.config.js # Сборка плагина
├── compile.sh # Скрипт компиляции
├── debug.sh # Скрипт отладки
├── nodemon.json # Конфигурация dev-режима
├── src/
│ ├── mixins/ # Пользовательские миксины (опционально)
│ │ ├── params.ts
│ │ └── ipc.ts
│ ├── my-plugin.ts # Основной файл плагина
│ └── my-plugin.json # Метаданные плагина для BARY
├── core/ # Базовые классы (наследуются)
│ ├── base-module.ts
│ └── base-driver-module.ts
├── enums/
│ └── EventTypes.ts # Типы событий
├── lib/ # Вспомогательные библиотеки
│ ├── better-queue/ # Система очередей
│ ├── foibles/ # Система миксинов
│ ├── require-ex.ts # Автоматическая установка зависимостей
│ └── shared.functions.ts
├── templates/ # Конфигурационные файлы устройств (опционально)
│ └── device-config.json
└── dist/ # Скомпилированные файлы

package.json

{
"name": "bary-plugin-mydevice",
"version": "1.0.0",
"main": "src/my-plugin.ts",
"scripts": {
"build": "webpack --display_modules",
"debug": "nodemon --config nodemon.json"
},
"dependencies": {
"async-mutex": "^0.5.0",
"moment": "^2.27.0",
"node-ipc": "^9.1.1",
"winston": "^3.17.0",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/node": "^14.14.34",
"ts-loader": "^8.0.14",
"ts-node": "^10.3.0",
"typescript": "^4.1.3",
"webpack": "^4.46.0",
"webpack-cli": "^3.2.3"
}
}

my-plugin.json

Метаданные и конфигурация плагина для BARY:

{
"name": "My Device Plugin",
"type": 1,
"icon": "hub",
"class_name": "my-plugin",
"module": "my-plugin",
"manufacturer": "Company Name",
"cloud": false,
"support_autoupdate": false,
"dynamic_capabilities": true,
"mac_address": false,
"capabilities": [],
"sort_index": 4,
"sub_devices": [
{
"class_name": "my-plugin.subdevice",
"name": "My SubDevice",
"dynamic_capabilities": true,
"mac_address": false,
"support_autoupdate": true,
"autoupdate_interval": 500,
"selectable": true,
"connect_config": true,
"type": 39,
"sort_index": 1,
"settings": [
{
"key": "identifier",
"name": "Device ID",
"type": "text",
"required": true
}
],
"commands": [
{
"command": "reset"
}
]
}
],
"settings": [
{
"key": "port",
"name": "Port",
"type": "text",
"required": true,
"defaultValue": "/dev/ttyUSB0"
},
{
"key": "baud_rate",
"name": "Baud rate",
"type": "select",
"items": [
{"id": 9600, "title": "9600"},
{"id": 19200, "title": "19200"},
{"id": 115200, "title": "115200"}
],
"defaultValue": 9600
},
{
"key": "enable_logging",
"name": "Enable logging",
"type": "checkbox"
}
],
"commands": [
{
"command": "scan_devices"
},
{
"command": "reset"
}
],
"display": [
{
"functions": [
{
"title": "Scan Mode",
"type": "scan_mode",
"view": "buttons",
"model": "device.:ident.status.scan_mode",
"buttons": [
{"value": "auto", "title": "Automatic"},
{"value": "manual", "title": "Manual"}
],
"data": {
"ident": ":ident",
"command": "scan_mode",
"value": ":scan_mode"
}
}
]
}
],
"dependencies": {
"my-device-sdk": "^2.0.0"
}
}

Основные поля:

  • name — отображаемое имя плагина
  • type — уникальный тип устройства (число)
  • icon — иконка плагина
  • class_name — техническое имя класса
  • module — имя модуля
  • manufacturer — производитель (опционально)
  • cloud — поддержка облака
  • support_autoupdate — автообновление статуса
  • dynamic_capabilities — capabilities генерируются в runtime
  • mac_address — требуется MAC-адрес
  • disable_display_sort — отключить сортировку отображения
  • capabilities — статические возможности устройства
  • sub_devices — конфигурация дочерних устройств
  • settings — статические настройки плагина
  • commands — команды плагина
  • display — кастомизация UI (опционально)
  • dependencies — NPM-зависимости (устанавливаются автоматически)

Базовые классы

baseModule

Базовый класс для всех плагинов, обеспечивает IPC коммуникацию с BARY.

Основные свойства:

  • config — конфигурация приложения
  • ipc — IPC клиент для связи с BARY
  • events — массив зарегистрированных событий
  • requestId — счетчик запросов
  • external_driver — флаг внешнего драйвера

Основные методы:

// Отправка запроса с ожиданием ответаrequest(eventName: string,params: object): Promise<any>// Отправка запроса без ожидания ответаrequestEx(eventName: string,params: object): void// Логированиеlog(message: any): voiderror(message: any): void// Динамическая загрузка npm модулейrequire(ident: string,require: boolean): Promise<any>

baseDriverModule

Расширяет baseModule дополнительными методами для работы с драйверами устройств.

Дополнительные свойства:

  • device_id — ID устройства
  • params — параметры устройства
  • config — конфигурация устройства
  • logger — Winston logger для записи логов
  • ident — идентификатор устройства
  • appDevices — список устройств приложения
  • statusCache — кэш статусов для оптимизации
  • pluginTemplate — метаданные плагина из JSON

Жизненный цикл:

// 1. Инициализация плагинаinitDeviceEx(resolve,reject): void// 2. Подключение к устройствуconnectEx(resolve,reject): void// 3. Обработка командcommandEx(command,value,params,options,resolve,reject,status): void// 4. Получение списка дочерних устройствgetSubDevicesEx(resolve,reject,zones): void// 5. Обработка событий от подписанных устройств (опционально)onSubscribeDevice(params: any): void

Дополнительные методы:

// Создание/обновление дочернего устройстваcheckSubDevice(model,key,name,params,zone_id): Promise<any>// Публикация событийpublish(eventType, ...params): voidpublishStatus(eventType,status): void// Формирование имени события для статусаeventTypeStatus(className,identifier?,key?): string// Подписка на события другого устройстваsubscribeDevice(ident,eventType): void// УведомленияsendNotify(message,options?): voidsendNotifyEx(body): voidsendPushNotification(message,email,title): void// Работа с устройствамиgetDevices(params?): Promise<Device[]>deviceCommand(ident,command,data,value): Promise<any>deviceEvent(ident,event,data): Promise<any>// Работа с БДgetTable(table,options): Promise<any[]>createTable(table,options): Promise<any>updateTable(table,options,where): Promise<any>getConfig(): Promise<any>getDeviceCustomData(ident,custom_data_id): Promise<any>// Загрузка templatesloadTemplate(ident,name,options?): any// ОчередиstartQueue(ident): voidcountQueue(ident): voiddoneQueue(ident,resolve,reject,inc?,error?): void

Расширение функциональности через Mixins

Что такое Mixins?

BARY использует библиотеку foibles для создания миксинов — модульного способа расширения функциональности классов.

Создание миксина

// src/mixins/params.tsconst{Mixin}=require('../../lib/foibles');exportconstDEVICE_ID_INC=10000;exportconstParams=Mixin(parent=>classParamsextendsparent{capabilities=[];settings_ex=[];getheater_list(){returnthis.params?.heater_list||[];}getheater_list_devices(){returnthis.heater_list ? this.appDevices.filter(item=>this.heater_list.find(item1=>item1===item.ident)) : [];}getDeviceParam(params,device,key,defaultValue){returnparams[`${device.ident}_${key}`]!==undefined ? params[`${device.ident}_${key}`] : defaultValue;}updateCapabilities(){this.capabilities=[];this.heater_list_devices.forEach(device=>{this.capabilities.push({ident: 'power',index: DEVICE_ID_INC+device.id,display_name: `${device.name} (${device.zone_name})`,options: {info: `info_${DEVICE_ID_INC+device.id}`,ignore_changes: true}});});}});

Использование миксинов

import{baseDriverModule}from'../core/base-driver-module';import{Params,DEVICE_ID_INC}from'./mixins/params';import{IPC}from'./mixins/ipc';// Расширение класса несколькими миксинамиclassMyPluginextendsbaseDriverModule.with(Params,IPC){connectEx(resolve,reject){this.getDevices({currentStatus: true}).then(devices=>{this.appDevices=devices;// Использование методов из миксина Paramsthis.updateCapabilities();// Доступ к геттерам миксинаthis.heater_list_devices.forEach(device=>{console.log(device.name);});// Публикация capabilitiesthis.publish(this.eventTypeStatus(this.pluginTemplate.class_name,this.id),{capabilities: this.capabilities,settings_ex: this.settings_ex});resolve({});});}}constapp=newMyPlugin();

Templates — конфигурационные файлы устройств

Создание templates

Плагины могут хранить конфигурационные файлы в папке /templates/:

my-plugin/
└── templates/
├── device1.json
├── device2.json
└── conditioner.json

Пример template

{
"manufacturer": "Philio Technology Corp",
"manufacturerId": "0x013c",
"label": "PAR01",
"description": "Conditioner",
"devices": [
{
"productType": "0x0106",
"productId": "0x8290"
}
],
"firmwareVersion": {
"min": "0.0",
"max": "255.255"
},
"paramInformation": [
{
"#": "25",
"label": "Learn mode",
"valueSize": 2,
"minValue": 0,
"maxValue": 2048,
"defaultValue": 1
},
{
"#": "27",
"label": "Temperature",
"valueSize": 2,
"minValue": 16,
"maxValue": 30,
"defaultValue": 22
}
]
}

Загрузка template

// В методе плагинаconsttemplate=this.loadTemplate('my-plugin','conditioner.json');console.log(template.manufacturer);// "Philio Technology Corp"// С опциями (если template — это функция)consttemplate=this.loadTemplate('my-plugin','device-template',{deviceId: 123});

Создание плагина

Минимальный пример

import{baseDriverModule}from'../core/base-driver-module';import{EventTypes}from'../enums/EventTypes';classMyPluginextendsbaseDriverModule{/** * Инициализация плагина */initDeviceEx(resolve,reject){super.initDeviceEx(()=>{this.app.log('Плагин инициализирован');// Создание имени события для статусаthis.eventName=this.eventTypeStatus(this.pluginTemplate.class_name,this.id);resolve({});},reject);}/** * Подключение к устройству */connectEx(resolve,reject){this.app.log('Подключение к устройству...');// Получение списка устройствthis.getDevices({currentStatus: true}).then(devices=>{this.appDevices=devices;// Подписка на события устройствthis.appDevices.forEach(device=>{this.subscribeDevice(device.ident,'changed_power');});// Публикация динамических capabilitiesconstcapabilities=[];capabilities.push({ident: 'power',index: 1,display_name: 'Включить/выключить',options: {hideTitle: false}});this.publish(this.eventName,{capabilities});});resolve({});}/** * Обработка команд */commandEx(command,value,params,options,resolve,reject,status){this.app.log('Команда:',command,'Значение:',value);// Проверка прав администратора (если нужно)if(command==='admin_action'&&options&&!options.is_admin){returnreject({message: 'Access Denied!'});}switch(command){case'on':
// Включить устройствоresolve({state: 'on'});break;case'off':
// Выключить устройствоresolve({state: 'off'});break;case'status':
// Получить статусresolve({connected: true,state: 'on'});break;default:
reject({message: 'Неизвестная команда'});}}/** * Обработка событий от подписанных устройств */onSubscribeDevice(params: any){constdevice=this.appDevices.find(item=>item.ident===params.ident);if(device){device.currentStatus=params.currentStatus;// Реакция на изменение статусаthis.handleDeviceUpdate(device);}}}// Создание экземпляра плагинаconstapp=newMyPlugin();app.logging=true;

Динамические Capabilities

Capabilities можно генерировать динамически в методе connectEx() или при изменении конфигурации.

Публикация capabilities

connectEx(resolve,reject){constcapabilities=[];// Простой переключательcapabilities.push({ident: 'power',index: 1,display_name: 'Управление отоплением',options: {hideTitle: false,hideSeparator: false,hideBottomPadding: false,ignore_changes: false,read_only: false}});// Текстовое поле с измерениемcapabilities.push({ident: 'text',index: 2,display_name: 'Температура',options: {measure: '°C',read_only: true}});// Ползунок (range)capabilities.push({ident: 'target_temperature',index: 3,display_name: 'Желаемая температура',options: {minValue: 5,maxValue: 30,stepValue: 0.5,hideTitle: true}});// Capability с информационным полемcapabilities.push({ident: 'power',index: 10,display_name: 'Насос 1',options: {info: 'info_10',// Связано с status.info_10ignore_changes: true}});// Заголовокcapabilities.push({ident: 'title',index: 100,options: {title_only: 'Климат-контроль'},hide_background: true,hide_bottom_padding: true,hide_separator: true});// Capability для добавления/удаления устройствcapabilities.push({ident: 'power',index: 1,display_name: 'Add Z-Wave device',options: {link_devices: true}});capabilities.push({ident: 'power',index: 2,display_name: 'Remove Z-Wave device',options: {unlink_devices: true}});// Публикацияthis.publish(this.eventTypeStatus(this.pluginTemplate.class_name,this.id),{capabilities});resolve({});}

Доступные опции capabilities

  • hideTitle — скрыть заголовок
  • hideSeparator — скрыть разделитель
  • hideBottomPadding — скрыть отступ снизу
  • ignore_changes — игнорировать изменения
  • read_only — только для чтения
  • info — связь с информационным полем (например, info_10 связано с status.info_10)
  • measure — единица измерения (например, °C, %, W)
  • minValue, maxValue, stepValue — для ползунков (range)
  • value_on, value_off — значения для переключателей
  • title_only — только заголовок без фона (для ident: 'title')
  • link_devices, unlink_devices — для добавления/удаления устройств

Динамические Settings (settings_ex)

Settings_ex позволяют создавать настройки с фильтрацией устройств по командам и типам.

Публикация settings_ex

connectEx(resolve,reject){constsettings_ex=[];// Выбор устройства с фильтрацией по командамsettings_ex.push({key: 'temperature_sensor',name: 'Датчик температуры',type: 'select',items: [],driver_support: [// Фильтр по поддерживаемым командам'supportTemperature','supportTemperatureEx'],multi: false// Одиночный выбор});// Множественный выбор устройствsettings_ex.push({key: 'heater_list',name: 'Список обогревателей',type: 'select',items: [],driver_support: ['supportPower','supportPowerEx'],driver_types: [5],// Фильтр по типу драйвераmulti: true// Множественный выбор});// Выбор комнатыsettings_ex.push({key: 'zone_id',name: 'Комната',type: 'select',items: [],zone_support: ['rooms']// Показывать только комнаты});// Условная видимостьsettings_ex.push({key: 'advanced_options',name: 'Дополнительные опции',type: 'text',visibleField: 'enable_advanced',// Показывать только еслиvisibleFieldValue: true// enable_advanced = true});// Текстовое поле с значением по умолчаниюsettings_ex.push({key: 'interval',name: 'Интервал обновления (мин)',type: 'text',defaultValue: 5});// Заголовок (группировка настроек)settings_ex.push({key: 'group_heating',name: 'Настройки отопления',type: 'title'});// Динамическое создание настроек для каждого устройстваthis.heater_list_devices.forEach(device=>{settings_ex.push({key: `${device.ident}_kp`,name: `${device.name} - Пропорциональная составляющая`,type: 'text',defaultValue: 0.5});});// Публикация вместе с capabilitiesthis.publish(this.eventTypeStatus(this.pluginTemplate.class_name,this.id),{capabilities, settings_ex});resolve({});}

Доступные опции settings_ex

  • key — ключ настройки
  • name — отображаемое имя
  • type — тип поля (text, select, checkbox, title)
  • items — элементы для select
  • defaultValue — значение по умолчанию
  • required — обязательное поле
  • driver_support — фильтрация устройств по поддерживаемым командам
  • driver_types — фильтрация устройств по типу
  • multi — множественный выбор (для select)
  • zone_support — фильтрация зон (['rooms'] — только комнаты)
  • visibleField — поле, от которого зависит видимость
  • visibleFieldValue — значение поля для отображения
  • description — описание настройки
  • group — группа настроек (например, settingsDevice)

API методы

Работа с устройствами

Создание дочернего устройства

checkSubDevice(model: string,// Модель устройства (например, 'zigbee2mqtt.light')key: string,// Уникальный ключname: string,// Название устройстваparams: object,// Параметры устройстваzone_id: number // ID зоны (null для автоматического определения)): Promise<any>

Параметры устройства:

{icon: 'light',// Иконка устройстваidentifier: 'unique_id',// Уникальный идентификаторcapabilities: [// Возможности устройства{index: 0,property: 'state',ident: 'state',display_name: 'Состояние',access: 'rw',// r=read, w=write, rw=read+writehomekit: true,// Поддержка HomeKityandex: true,// Поддержка Яндекс Алисаsber: true,// Поддержка Сберoptions: {value_on: true,value_off: false,minValue: 0,maxValue: 100,stepValue: 1}}],settings_ex: [// Динамические настройки устройства{key: 'parameter_1',name: 'Parameter 1',type: 'text',defaultValue: 10}],parent_identifier: 123// ID родительского устройства}

Доступные иконки:

  • Освещение:light, chandelier, rgb_lamp, rgb_strip, rgb_led, table_lamp, spotlight, sconce, facade_light
  • Развлечения:tv, gamepad, music_note, speaker
  • Безопасность:camera, security, intercom, doorlock, night, smoke, gas_leak, leak
  • Дом:home, gates, gate1, gate2, gate3, gate4, socket, infrared, faucet, valve, pump, hub
  • Шторы/жалюзи:view_column, louvers, marquise
  • Климат:floor_heater, heater, heater1, convector, humidifier, ac_unit, fan, dryer, cooling, anti-icing
  • Датчики:co2, voc, p2, multisensor, temp_sensor, humidity_sensor, door_sensor, counter, motion
  • Бытовая техника:kettle, exhaust_hood, fridge, washing_machine, microwave, dish_washer, oven, stove, vacuum_cleaner, pool, squirt

Типы плагинов (driver_types):

BARY поддерживает 52 типа плагинов для различных устройств и сервисов:

  • Шлюзы и сервисы: Шлюзы (1), Облачные сервисы (2), Провайдеры услуг (19), Система (39), Внешние API для камеры (43), Музыкальные сервисы (45)
  • Медиа устройства: Игровые приставки (7), Ресиверы (8), Телевизоры (9), ТВ-приставки (18), Умные колонки (37), Интерактивные панели (50)
  • Умный дом базовый: Умные розетки (3), Выключатели (12), Релейные модули (14), Эмуляторы IR/RF (16), Умные лампочки (22), Освещение (25), RGB Контроллеры (40), Диммеры (44)
  • Климат-контроль: Термостаты (13), Кондиционеры (17), Системы вентиляции (33), Умные вентиляторы (41), Климат-контроль (47), Увлажнители (6)
  • Датчики: Датчики температуры (11), Датчики влажности (23), Датчики освещенности (24), Датчики протечки (26), Датчики движения (27), Датчики напряжения (28), Датчики УФ (29), Датчики открытия окон и дверей (30), Комбинированные датчики (31), Детекторы дыма (36), Мониторы качества воздуха (21)
  • Безопасность: Камеры (5), Дверные замки (32), Домофоны (42), Распознавание ГРЗ (48), СКУД (49)
  • Бытовая техника: Пылесосы (4), Приводы штор (38), Уход за растениями (20)
  • Инженерные системы: Счетчики электроэнергии (15), Водосчетчики (35), Умные контроллеры (34), Модули ввода-вывода (46), Модули управления устройствами (51)
  • Голосовое управление: Голосовые ассистенты (52), Погода (10)

Получение списка устройств

getDevices(params?: object): Promise<Device[]>

Пример:

constdevices=awaitthis.getDevices({currentStatus: true});devices.forEach(device=>{this.app.log('Устройство:',device.name,'Статус:',device.currentStatus);});

Подписка на события устройства

subscribeDevice(ident: string,eventType: string): void

Пример:

// Подписка на изменение питанияthis.subscribeDevice('device-001','changed_power');// Обработка в onSubscribeDevice()onSubscribeDevice(params: any){constdevice=this.appDevices.find(item=>item.ident===params.ident);if(device){device.currentStatus=params.currentStatus;this.app.log('Статус изменился:',device.name,params.currentStatus);}}

Отправка команды другому устройству

deviceCommand(ident: string,// Идентификатор устройстваcommand: string,// Командаdata: object,// Данныеvalue: any // Значение): Promise<any>

Пример:

awaitthis.deviceCommand('light_bedroom','on',{},true);

Публикация событий

Формирование имени события

eventTypeStatus(className: string,identifier?: string,key?: string): string

Пример:

consteventName=this.eventTypeStatus('my-plugin','device-001','temperature');// Результат: "status->my-plugin->device-001->temperature"// Для плагинаconsteventName=this.eventTypeStatus(this.pluginTemplate.class_name,this.id);// Результат: "status->my-plugin->driver-123"

Публикация статуса устройства

publishStatus(eventType: EventTypes,status: object): void

Пример:

// Отправка данных с датчикаthis.publishStatus(EventTypes.UpdateTemperature,{temperature_living_room: 22.5,humidity_living_room: 45,connected: true});

Оптимизация публикации:

Метод publishStatus автоматически:

  • Публикует только измененные данные
  • Отправляет числовые данные при изменении >10% или раз в 15 секунд
  • Всегда отправляет события датчиков движения, открытия и т.д.

Прямая публикация событий

publish(eventType: EventTypes|string, ...params: any[]): void

Пример:

this.publish(EventTypes.ChangedMotion,{parent_identifier: this.device_id,motion_sensor_1: true});// Публикация capabilities и settings_exthis.publish(this.eventTypeStatus(this.pluginTemplate.class_name,this.id),{capabilities, settings_ex,displays: [...]});

Уведомления

Отправка уведомления пользователю

sendNotify(message: string,options?: object): void

Пример:

this.sendNotify('Устройство подключено успешно!');

Системные уведомления (sendNotifyEx)

sendNotifyEx(body: object): void

Пример:

// Обновление устройстваthis.sendNotifyEx({system: true,type: 'device-update',ident: this.ident});// Обновление настроекthis.sendNotifyEx({system: true,type: 'settings-update',ident: this.ident});

Push-уведомление

sendPushNotification(message: string,email: string,title: string): void

Пример:

this.sendPushNotification('Обнаружено движение в гостиной','user@example.com','Датчик движения');

Работа с данными

Получение конфигурации

getConfig(): Promise<any>

Работа с базой данных

// Получение записейgetTable(table: string,options: object): Promise<any[]>// Создание записиcreateTable(table: string,options: object): Promise<any>// Обновление записейupdateTable(table: string,options: object,where: object): Promise<any>

Пример:

// Получение всех устройств типа 'light'constlights=awaitthis.getTable('devices',{where: {type: 'light'}});// Создание записиawaitthis.createTable('custom_data',{device_id: 123,key: 'last_seen',value: newDate().toISOString()});// Обновлениеawaitthis.updateTable('devices',{name: 'Updated Name'},{where: {id: 123}});

Облачные запросы

cloudRequest(params: object): Promise<any>

Работа с очередями (Better-queue)

// Создание очереди запросовconstQueue=require('../lib/better-queue/queue');this.requestQueue=newQueue((task,callback)=>{this.deviceCommand(task.ident,task.command,{},task.value).then(()=>{callback(null,{});}).catch(error=>{callback(error);});},{concurrent: 1,// Последовательная обработкаmaxRetries: 3});// Добавление задачи в очередьthis.requestQueue.push({ident: 'device-001',command: 'set_power',value: true},(error,result)=>{if(error){console.error('Error:',error);}else{console.log('Success:',result);}});// Встроенные методы для простых очередейthis.startQueue('commands');this.countQueue('commands');this.doneQueue('commands',resolve,reject);

Типы событий (EventTypes)

Устройства

  • DeviceCreate — создание устройства
  • DeviceUpdate — обновление устройства
  • DeviceDelete — удаление устройства
  • DeviceConnect — подключение устройства
  • DeviceDiscover — обнаружение нового устройства

Обновления данных

  • UpdateTemperature — обновление температуры
  • UpdateThermostatTemp — обновление температуры термостата
  • UpdatePower — обновление мощности
  • UpdateAlert — предупреждение

События датчиков

  • ChangedMotion — изменение датчика движения
  • ChangedMagnet — изменение геркона (открытие/закрытие)
  • ChangedMagnetChange — изменение геркона с передачей изменений
  • ChangedPower — изменение состояния питания
  • ChangedPowerChange — изменение состояния питания с передачей изменений
  • ChangedPowerLoad — изменение нагрузки
  • ChangedAction — пользовательское действие
  • ChangedLeakChange — обнаружение протечки
  • ChangedInputChange — изменение входа
  • ChangedCounterChange — изменение счетчика
  • ChangedChannel — изменение канала
  • ChangedChannel0 — изменение канала 0
  • ChangedChannel1 — изменение канала 1

MQTT

  • MqttMessage — сообщение MQTT

Статистика

  • NewStats — новая статистика
  • NewStatsEx — расширенная статистика

Wi-Fi

  • WiFiConnect — подключение Wi-Fi
  • WiFiConnected — Wi-Fi подключен
  • WiFiNetworkFound — сеть Wi-Fi найдена

Приложение

  • ApplicationReady — приложение готово
  • ApplicationCreated — приложение создано
  • ApplicationGetDevices — получение устройств приложения
  • ApplicationDeviceCommand — команда устройству
  • ApplicationAddDeviceQueue — добавление устройства в очередь
  • ApplicationAddScanQueue — добавление сканирования в очередь
  • ApplicationDriverReady — драйвер готов
  • NewEvent — новое событие

База данных

  • DatabaseReady — БД готова
  • DatabaseConnected — БД подключена
  • DatabaseGetAllItems — получение всех элементов
  • DatabaseUpdateItem — обновление элемента
  • DatabaseUpdateDeviceParams — обновление параметров устройства
  • DatabaseCreateItem — создание элемента
  • DatabaseQuery — запрос к БД
  • DatabaseUpdate — обновление БД
  • DatabaseQueueQuery — запрос в очереди БД

Прочее

  • CheckSubDevice — проверка дочернего устройства
  • Publish — публикация
  • Notify — уведомление
  • UpdateSensor — обновление датчика
  • RemoveSensor — удаление датчика
  • UpdateSensorEx — расширенное обновление датчика

Примеры плагинов

Пример 1: Плагин для HTTP устройств

import{baseDriverModule}from'../core/base-driver-module';import{EventTypes}from'../enums/EventTypes';importaxiosfrom'axios';classHttpDevicePluginextendsbaseDriverModule{privatebaseUrl: string;initDeviceEx(resolve,reject){super.initDeviceEx(()=>{// Получаем URL из параметровthis.baseUrl=this.params.url||'http://192.168.1.100';this.app.log('HTTP устройство:',this.baseUrl);resolve({});},reject);}connectEx(resolve,reject){// Проверяем доступность устройстваaxios.get(`${this.baseUrl}/status`).then(response=>{this.app.log('Устройство онлайн');this.startPolling();resolve({});}).catch(error=>{reject({message: 'Устройство недоступно'});});}startPolling(){setInterval(()=>{axios.get(`${this.baseUrl}/sensors`).then(response=>{this.publishStatus(EventTypes.UpdateTemperature,{temperature_sensor: response.data.temperature,humidity_sensor: response.data.humidity,connected: true});}).catch(error=>{this.app.error('Ошибка получения данных:',error.message);});},10000);// Каждые 10 секунд}commandEx(command,value,params,options,resolve,reject,status){switch(command){case'on':
axios.post(`${this.baseUrl}/control`,{power: 'on'}).then(()=>resolve({state: 'on'})).catch(error=>reject({message: error.message}));break;case'off':
axios.post(`${this.baseUrl}/control`,{power: 'off'}).then(()=>resolve({state: 'off'})).catch(error=>reject({message: error.message}));break;case'status':
axios.get(`${this.baseUrl}/status`).then(response=>resolve(response.data)).catch(error=>reject({message: error.message}));break;default:
reject({message: 'Неизвестная команда'});}}}constapp=newHttpDevicePlugin();app.logging=true;

Пример 2: Плагин с динамическими capabilities и миксинами

import{baseDriverModule}from'../core/base-driver-module';import{EventTypes}from'../enums/EventTypes';import{Params}from'./mixins/params';classDashboardPluginextendsbaseDriverModule.with(Params){appDevices=[];initDeviceEx(resolve,reject){super.initDeviceEx(()=>{this.app.log('Dashboard plugin initialized');this.eventName=this.eventTypeStatus(this.pluginTemplate.class_name,this.id);resolve({});},reject);}connectEx(resolve,reject){this.getDevices({currentStatus: true}).then(devices=>{this.appDevices=devices;// Подписка на события устройствthis.appDevices.forEach(device=>{this.subscribeDevice(device.ident,'changed_power');});// Динамическое создание capabilitiesthis.updateCapabilities();this.updateSettingsEx();// Публикация capabilities и settings_exthis.publish(this.eventName,{capabilities: this.capabilities,settings_ex: this.settings_ex});resolve({});});}updateCapabilities(){this.capabilities=[];constdeviceCount=this.params.device_count||0;// Создание capabilities для каждого устройстваfor(leti=0;i<deviceCount;i++){constdeviceIdent=this.params[`device_ident_${i}`];constdeviceType=this.params[`device_type_${i}`];if(deviceIdent){this.capabilities.push({ident: 'power',index: i+1,display_name: `Device ${i+1}`,options: {hideTitle: false,info: `info_${i}`,ignore_changes: false}});}}}updateSettingsEx(){this.settings_ex=[];constdeviceCount=this.params.device_count||4;// Заголовокthis.settings_ex.push({key: 'devices_group',name: 'Devices Configuration',type: 'title'});// Создание настроек для каждого устройстваfor(leti=0;i<deviceCount;i++){// Тип устройстваthis.settings_ex.push({key: `device_type_${i}`,name: `Device ${i+1} Type`,type: 'select',items: [{id: 'switch',title: 'Switch'},{id: 'sensor',title: 'Sensor'},{id: 'light',title: 'Light'}],defaultValue: 'switch'});// Выбор устройства с фильтрациейthis.settings_ex.push({key: `device_ident_${i}`,name: `Device ${i+1}`,type: 'select',items: [],driver_support: ['supportPower'],multi: false});}}onSubscribeDevice(params: any){// Обработка событий от подписанных устройствconstdevice=this.appDevices.find(item=>item.ident===params.ident);if(device){device.currentStatus=params.currentStatus;this.app.log('Device status updated:',device.name,params.currentStatus);// Публикация обновленного статусаthis.publishStatus(EventTypes.UpdatePower,{[`status_${device.ident}`]: params.currentStatus.power});}}commandEx(command,value,params,options,resolve,reject,status){// Проверка прав администратораif(command==='reset'&&options&&!options.is_admin){returnreject({message: 'Access Denied!'});}switch(command){case'update_config':
// Обновление конфигурацииthis.updateCapabilities();this.updateSettingsEx();this.publish(this.eventName,{capabilities: this.capabilities,settings_ex: this.settings_ex});// Системное уведомление об обновлении настроекthis.sendNotifyEx({system: true,type: 'settings-update',ident: this.ident});resolve({success: true});break;case'status':
resolve({connected: true,devices_count: this.capabilities.length});break;default:
reject({message: 'Неизвестная команда'});}}}constapp=newDashboardPlugin();app.logging=true;

Пример 3: Плагин-шлюз с дочерними устройствами

import{baseDriverModule}from'../core/base-driver-module';import{EventTypes}from'../enums/EventTypes';classGatewayPluginextendsbaseDriverModule{privatedevices: Map<string,any>=newMap();initDeviceEx(resolve,reject){super.initDeviceEx(()=>{this.app.log('Шлюз инициализирован');resolve({});},reject);}connectEx(resolve,reject){// Сканирование устройствthis.scanDevices().then(()=>{resolve({});});}asyncscanDevices(){// Обнаружение устройств (например, через Zigbee, Z-Wave и т.д.)constfoundDevices=[{id: '0x123',type: 'light',name: 'Свет в гостиной'},{id: '0x456',type: 'sensor',name: 'Датчик температуры'}];for(constdeviceoffoundDevices){awaitthis.registerDevice(device);}}asyncregisterDevice(device: any){letmodel,icon,capabilities;if(device.type==='light'){model='gateway.light';icon='light';capabilities=[{property: 'state',ident: 'state',display_name: 'Включено',access: 'rw',homekit: true,yandex: true,options: {value_on: true,value_off: false}},{property: 'brightness',ident: 'brightness',display_name: 'Яркость',access: 'rw',homekit: true,options: {minValue: 0,maxValue: 100,stepValue: 1}}];}elseif(device.type==='sensor'){model='gateway.temperature';icon='temp_sensor';capabilities=[{property: 'temperature',ident: 'temperature',display_name: 'Температура',access: 'r',scale: '°C'}];}constresult=awaitthis.checkSubDevice(model,device.id,device.name,{icon,identifier: device.id, capabilities},null);this.devices.set(device.id,device);this.app.log('Зарегистрировано устройство:',device.name);}commandEx(command,value,params,options,resolve,reject,status){constdeviceId=options.ident;// Отправка команды конкретному дочернему устройствуif(this.devices.has(deviceId)){// Здесь отправка команды через протокол шлюзаthis.app.log('Команда для устройства',deviceId,':',command,value);resolve({success: true});}else{reject({message: 'Устройство не найдено'});}}getSubDevicesEx(resolve,reject,zones){// Возврат списка всех дочерних устройствconstdevices=Array.from(this.devices.values()).map(device=>({class_name: 'GatewayDevice',identifier: device.id,name: device.name,params: {icon: device.type==='light' ? 'light' : 'temp_sensor',identifier: device.id}}));resolve(devices);}}constapp=newGatewayPlugin();app.logging=true;

Отладка плагинов

Локальный запуск

Для ручного тестирования плагина:

constapp=newMyPlugin();app.logging=true;// Инициализация с тестовыми параметрамиapp.initDevice({params: {url: 'http://192.168.1.100',port: 80}}).then(()=>{// Подключениеapp.connect({id: 1}).then(()=>{// Тестовая командаapp.command({command: 'status',id: 1}).then(result=>{console.log('Результат:',result);});});});

Логирование

// Разные уровни логированияthis.app.log('Информация');// INFOthis.app.error('Ошибка');// ERRORthis.app.debug('Отладка');// DEBUGthis.app.info('Информация');// INFO// Логирование с автоматическим таймстампом и использованием памяти// Формат: HH:mm:ss [memory] сообщение

Логи сохраняются в ${params.log_path}/${module}-${id}.log с ротацией:

  • Максимальный размер файла: 10 МБ
  • Количество файлов: 5

Параметры запуска

# Запуск с логированием
node dist/my-plugin.js 123 logging=true
# Удаленное подключение к BARY
node dist/my-plugin.js 123 host=192.168.1.100 port=8000

Лучшие практики

1. Обработка ошибок

commandEx(command,value,params,options,resolve,reject,status){try{// Ваш кодresolve(result);}catch(error){this.app.error('Ошибка выполнения команды:',error.message);reject({message: error.message,ignore: false});}}

2. Переподключение при ошибках

connectEx(resolve,reject){consttryConnect=(attempts=0)=>{this.connectToDevice().then(()=>resolve({})).catch(error=>{if(attempts<3){this.app.log(`Попытка переподключения ${attempts+1}/3`);setTimeout(()=>tryConnect(attempts+1),5000);}else{reject({message: 'Не удалось подключиться'});}});};tryConnect();}

3. Оптимизация публикации данных

// Используйте publishStatus для автоматической оптимизацииthis.publishStatus(EventTypes.UpdateTemperature,{temperature: 22.5,// Отправится только при изменении >10%humidity: 45// или раз в 15 секунд});// Для важных событий используйте publish напрямуюthis.publish(EventTypes.ChangedMotion,{motion: true// Отправится сразу});

4. Использование миксинов для модульности

// Разделяйте функциональность на миксины// mixins/params.ts — работа с параметрами// mixins/ipc.ts — дополнительная IPC логика// mixins/servo.ts — управление сервоприводамиclassMyPluginextendsbaseDriverModule.with(Params,IPC,Servo){// Чистый и модульный код}

5. Управление зависимостями

В my-plugin.json указывайте только специфичные для вашего плагина зависимости:

{
"dependencies": {
"my-device-sdk": "^2.0.0",
"zwave-js": "^9.2.2"
}
}

BARY автоматически установит их при первом запуске плагина через RequireEx.

6. Использование очередей для последовательных операций

// Создайте очередь для команд устройствуconstQueue=require('../lib/better-queue/queue');this.requestQueue=newQueue((task,callback)=>{this.deviceCommand(task.ident,task.command,{},task.value).then(()=>callback(null,{})).catch(error=>callback(error));},{concurrent: 1,maxRetries: 3});// Используйте очередь вместо прямых вызововthis.requestQueue.push({ident: device.ident,command: 'set_power',value: true});

Сборка и развертывание

Компиляция

# Установка зависимостей
npm install
# Сборка
npm run build
# Отладка с автоперезагрузкой
npm run debug

Создание релиза

# Компиляция и упаковка в ZIP
./compile.sh 1.0.0
# С копированием на сервер
./compile.sh 1.0.0 user@server:/path/to/plugins

Результат — ZIP-архив в папке release/ с структурой:

plugins/
├── my-plugin.js
└── my-plugin.json

Поддержка и документация


Версия документации: 2.0 (2025-11-19)
Дополнения: Mixins, Templates, Динамические capabilities, Settings_ex, Better-queue, sendNotifyEx, eventTypeStatus, subscribeDevice, onSubscribeDevice

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages