| layout | title | date | categories | description |
|---|---|---|---|---|
article |
Plugin API |
2025-11-19 12:00:00 +0300 |
bary install |
Полное руководство по созданию плагинов для BARY с продвинутыми возможностями |
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/ # Скомпилированные файлы
{
"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"
}
}Метаданные и конфигурация плагина для 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 генерируются в runtimemac_address— требуется MAC-адресdisable_display_sort— отключить сортировку отображенияcapabilities— статические возможности устройстваsub_devices— конфигурация дочерних устройствsettings— статические настройки плагинаcommands— команды плагинаdisplay— кастомизация UI (опционально)dependencies— NPM-зависимости (устанавливаются автоматически)
Базовый класс для всех плагинов, обеспечивает IPC коммуникацию с BARY.
Основные свойства:
config— конфигурация приложенияipc— IPC клиент для связи с BARYevents— массив зарегистрированных событийrequestId— счетчик запросовexternal_driver— флаг внешнего драйвера
Основные методы:
// Отправка запроса с ожиданием ответа
request(eventName: string, params: object): Promise<any>
// Отправка запроса без ожидания ответа
requestEx(eventName: string, params: object): void
// Логирование
log(message: any): void
error(message: any): void
// Динамическая загрузка npm модулей
require(ident: string, require: boolean): Promise<any>Расширяет 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): void
publishStatus(eventType, status): void
// Формирование имени события для статуса
eventTypeStatus(className, identifier?, key?): string
// Подписка на события другого устройства
subscribeDevice(ident, eventType): void
// Уведомления
sendNotify(message, options?): void
sendNotifyEx(body): void
sendPushNotification(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>
// Загрузка templates
loadTemplate(ident, name, options?): any
// Очереди
startQueue(ident): void
countQueue(ident): void
doneQueue(ident, resolve, reject, inc?, error?): voidBARY использует библиотеку foibles для создания миксинов — модульного способа расширения функциональности классов.
// src/mixins/params.ts
const {Mixin} = require('../../lib/foibles');
export const DEVICE_ID_INC = 10000;
export const Params = Mixin(parent => class Params extends parent {
capabilities = [];
settings_ex = [];
get heater_list() {
return this.params?.heater_list || [];
}
get heater_list_devices() {
return this.heater_list
? this.appDevices.filter(item =>
this.heater_list.find(item1 => item1 === item.ident)
)
: [];
}
getDeviceParam(params, device, key, defaultValue) {
return params[`${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';
// Расширение класса несколькими миксинами
class MyPlugin extends baseDriverModule.with(Params, IPC) {
connectEx(resolve, reject) {
this.getDevices({currentStatus: true}).then(devices => {
this.appDevices = devices;
// Использование методов из миксина Params
this.updateCapabilities();
// Доступ к геттерам миксина
this.heater_list_devices.forEach(device => {
console.log(device.name);
});
// Публикация capabilities
this.publish(
this.eventTypeStatus(this.pluginTemplate.class_name, this.id),
{capabilities: this.capabilities, settings_ex: this.settings_ex}
);
resolve({});
});
}
}
const app = new MyPlugin();Плагины могут хранить конфигурационные файлы в папке /templates/:
my-plugin/
└── templates/
├── device1.json
├── device2.json
└── conditioner.json
{
"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
}
]
}// В методе плагина
const template = this.loadTemplate('my-plugin', 'conditioner.json');
console.log(template.manufacturer); // "Philio Technology Corp"
// С опциями (если template — это функция)
const template = this.loadTemplate('my-plugin', 'device-template', {
deviceId: 123
});import {baseDriverModule} from '../core/base-driver-module';
import {EventTypes} from '../enums/EventTypes';
class MyPlugin extends baseDriverModule {
/**
* Инициализация плагина
*/
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');
});
// Публикация динамических capabilities
const capabilities = [];
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) {
return reject({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) {
const device = this.appDevices.find(item => item.ident === params.ident);
if (device) {
device.currentStatus = params.currentStatus;
// Реакция на изменение статуса
this.handleDeviceUpdate(device);
}
}
}
// Создание экземпляра плагина
const app = new MyPlugin();
app.logging = true;Capabilities можно генерировать динамически в методе connectEx() или при изменении конфигурации.
connectEx(resolve, reject) {
const capabilities = [];
// Простой переключатель
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_10
ignore_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({});
}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_ex позволяют создавать настройки с фильтрацией устройств по командам и типам.
connectEx(resolve, reject) {
const settings_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
});
});
// Публикация вместе с capabilities
this.publish(
this.eventTypeStatus(this.pluginTemplate.class_name, this.id),
{capabilities, settings_ex}
);
resolve({});
}key— ключ настройкиname— отображаемое имяtype— тип поля (text,select,checkbox,title)items— элементы дляselectdefaultValue— значение по умолчаниюrequired— обязательное полеdriver_support— фильтрация устройств по поддерживаемым командамdriver_types— фильтрация устройств по типуmulti— множественный выбор (дляselect)zone_support— фильтрация зон (['rooms']— только комнаты)visibleField— поле, от которого зависит видимостьvisibleFieldValue— значение поля для отображенияdescription— описание настройкиgroup— группа настроек (например,settingsDevice)
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+write
homekit: true, // Поддержка HomeKit
yandex: 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[]>Пример:
const devices = await this.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) {
const device = 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>Пример:
await this.deviceCommand('light_bedroom', 'on', {}, true);eventTypeStatus(
className: string,
identifier?: string,
key?: string
): stringПример:
const eventName = this.eventTypeStatus('my-plugin', 'device-001', 'temperature');
// Результат: "status->my-plugin->device-001->temperature"
// Для плагина
const eventName = 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_ex
this.publish(
this.eventTypeStatus(this.pluginTemplate.class_name, this.id),
{capabilities, settings_ex, displays: [...]}
);sendNotify(message: string, options?: object): voidПример:
this.sendNotify('Устройство подключено успешно!');sendNotifyEx(body: object): voidПример:
// Обновление устройства
this.sendNotifyEx({
system: true,
type: 'device-update',
ident: this.ident
});
// Обновление настроек
this.sendNotifyEx({
system: true,
type: 'settings-update',
ident: this.ident
});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'
const lights = await this.getTable('devices', {
where: {type: 'light'}
});
// Создание записи
await this.createTable('custom_data', {
device_id: 123,
key: 'last_seen',
value: new Date().toISOString()
});
// Обновление
await this.updateTable(
'devices',
{name: 'Updated Name'},
{where: {id: 123}}
);cloudRequest(params: object): Promise<any>// Создание очереди запросов
const Queue = require('../lib/better-queue/queue');
this.requestQueue = new Queue((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);DeviceCreate— создание устройстваDeviceUpdate— обновление устройстваDeviceDelete— удаление устройстваDeviceConnect— подключение устройстваDeviceDiscover— обнаружение нового устройства
UpdateTemperature— обновление температурыUpdateThermostatTemp— обновление температуры термостатаUpdatePower— обновление мощностиUpdateAlert— предупреждение
ChangedMotion— изменение датчика движенияChangedMagnet— изменение геркона (открытие/закрытие)ChangedMagnetChange— изменение геркона с передачей измененийChangedPower— изменение состояния питанияChangedPowerChange— изменение состояния питания с передачей измененийChangedPowerLoad— изменение нагрузкиChangedAction— пользовательское действиеChangedLeakChange— обнаружение протечкиChangedInputChange— изменение входаChangedCounterChange— изменение счетчикаChangedChannel— изменение каналаChangedChannel0— изменение канала 0ChangedChannel1— изменение канала 1
MqttMessage— сообщение MQTT
NewStats— новая статистикаNewStatsEx— расширенная статистика
WiFiConnect— подключение Wi-FiWiFiConnected— 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— расширенное обновление датчика
import {baseDriverModule} from '../core/base-driver-module';
import {EventTypes} from '../enums/EventTypes';
import axios from 'axios';
class HttpDevicePlugin extends baseDriverModule {
private baseUrl: 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: 'Неизвестная команда'});
}
}
}
const app = new HttpDevicePlugin();
app.logging = true;import {baseDriverModule} from '../core/base-driver-module';
import {EventTypes} from '../enums/EventTypes';
import {Params} from './mixins/params';
class DashboardPlugin extends baseDriverModule.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');
});
// Динамическое создание capabilities
this.updateCapabilities();
this.updateSettingsEx();
// Публикация capabilities и settings_ex
this.publish(this.eventName, {
capabilities: this.capabilities,
settings_ex: this.settings_ex
});
resolve({});
});
}
updateCapabilities() {
this.capabilities = [];
const deviceCount = this.params.device_count || 0;
// Создание capabilities для каждого устройства
for (let i = 0; i < deviceCount; i++) {
const deviceIdent = this.params[`device_ident_${i}`];
const deviceType = 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 = [];
const deviceCount = this.params.device_count || 4;
// Заголовок
this.settings_ex.push({
key: 'devices_group',
name: 'Devices Configuration',
type: 'title'
});
// Создание настроек для каждого устройства
for (let i = 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) {
// Обработка событий от подписанных устройств
const device = 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) {
return reject({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: 'Неизвестная команда'});
}
}
}
const app = new DashboardPlugin();
app.logging = true;import {baseDriverModule} from '../core/base-driver-module';
import {EventTypes} from '../enums/EventTypes';
class GatewayPlugin extends baseDriverModule {
private devices: Map<string, any> = new Map();
initDeviceEx(resolve, reject) {
super.initDeviceEx(() => {
this.app.log('Шлюз инициализирован');
resolve({});
}, reject);
}
connectEx(resolve, reject) {
// Сканирование устройств
this.scanDevices().then(() => {
resolve({});
});
}
async scanDevices() {
// Обнаружение устройств (например, через Zigbee, Z-Wave и т.д.)
const foundDevices = [
{id: '0x123', type: 'light', name: 'Свет в гостиной'},
{id: '0x456', type: 'sensor', name: 'Датчик температуры'}
];
for (const device of foundDevices) {
await this.registerDevice(device);
}
}
async registerDevice(device: any) {
let model, 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}
}
];
} else if (device.type === 'sensor') {
model = 'gateway.temperature';
icon = 'temp_sensor';
capabilities = [
{
property: 'temperature',
ident: 'temperature',
display_name: 'Температура',
access: 'r',
scale: '°C'
}
];
}
const result = await this.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) {
const deviceId = options.ident;
// Отправка команды конкретному дочернему устройству
if (this.devices.has(deviceId)) {
// Здесь отправка команды через протокол шлюза
this.app.log('Команда для устройства', deviceId, ':', command, value);
resolve({success: true});
} else {
reject({message: 'Устройство не найдено'});
}
}
getSubDevicesEx(resolve, reject, zones) {
// Возврат списка всех дочерних устройств
const devices = 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);
}
}
const app = new GatewayPlugin();
app.logging = true;Для ручного тестирования плагина:
const app = new MyPlugin();
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('Информация'); // INFO
this.app.error('Ошибка'); // ERROR
this.app.debug('Отладка'); // DEBUG
this.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=8000commandEx(command, value, params, options, resolve, reject, status) {
try {
// Ваш код
resolve(result);
} catch (error) {
this.app.error('Ошибка выполнения команды:', error.message);
reject({message: error.message, ignore: false});
}
}connectEx(resolve, reject) {
const tryConnect = (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();
}// Используйте publishStatus для автоматической оптимизации
this.publishStatus(EventTypes.UpdateTemperature, {
temperature: 22.5, // Отправится только при изменении >10%
humidity: 45 // или раз в 15 секунд
});
// Для важных событий используйте publish напрямую
this.publish(EventTypes.ChangedMotion, {
motion: true // Отправится сразу
});// Разделяйте функциональность на миксины
// mixins/params.ts — работа с параметрами
// mixins/ipc.ts — дополнительная IPC логика
// mixins/servo.ts — управление сервоприводами
class MyPlugin extends baseDriverModule.with(Params, IPC, Servo) {
// Чистый и модульный код
}В my-plugin.json указывайте только специфичные для вашего плагина зависимости:
{
"dependencies": {
"my-device-sdk": "^2.0.0",
"zwave-js": "^9.2.2"
}
}BARY автоматически установит их при первом запуске плагина через RequireEx.
// Создайте очередь для команд устройству
const Queue = require('../lib/better-queue/queue');
this.requestQueue = new Queue((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
- Официальный сайт: https://bary.io
- Email: support@bary.io
- Telegram: @bary_support
- Примеры плагинов: https://github.com/bary-io/plugins
Версия документации: 2.0 (2025-11-19)
Дополнения: Mixins, Templates, Динамические capabilities, Settings_ex, Better-queue, sendNotifyEx, eventTypeStatus, subscribeDevice, onSubscribeDevice