Перейти к содержанию

Навигация

Rule Examples: различия между версиями

15 318 байт добавлено ,  1 месяц назад
(не показано 36 промежуточных версий 6 участников)
Строка 1: Строка 1:
{{DISPLAYTITLE: Примеры правил}}
<languages/>
<languages/>
<translate>
<translate>
=== Слежение за контролом === <!--T:20-->
<!--T:255-->
{{DISPLAYTITLE: Примеры правил}}
 
== Общая информация ==
Здесь вы найдёте учебные примеры скриптов, написанных для движка правил '''[[wb-rules| wb-rules]]'''.
 
Алгоритмы в примерах предельно просты и не учитывают многих факторов которые могут возникнуть в реальности. Поэтому используйте эту библиотеку только как учебный материал, а не источник готовых скриптов для реальных проектов.
 
== Виртуальное устройство ==
 
Виртуальное устройство можно использовать для объединения каналов, задания особой логики для устройства или просто так для красоты.
 
Пример ниже создаст виртуальное устройство с именем '''deviceName''' и двумя контролами '''value''' и '''state'''. А благодаря правилу с '''whenChanged''', значение контрола '''state''' будет менять в зависимости от значение контрола '''value'''.
 
<syntaxhighlight lang="ecmascript">
deviceName = 'my-virtual-device';
 
defineVirtualDevice(deviceName, {
    title: {'en': 'My Virtual Device', 'ru': 'Мое виртуальное устройство'} ,
    cells: {
      value: {
        title: {'en': 'Value', 'ru': 'Значение'},
        type: "range",
        value: 1,
        max: 3,
        min: 1
      },
      state: {
        title: {'en': 'State', 'ru': 'Состояние'},
        type: "value",
        value: 1,
        enum:{
          1: {'en': 'Normal', 'ru': 'В норме'},
          2: {'en': 'Warning', 'ru': 'Внимание'},
          3: {'en': 'Crash', 'ru': 'Авария'}} 
      },
    }
});
 
defineRule({
  whenChanged: deviceName+"/value",
  then: function (newValue, devName, cellName) {
dev[deviceName+"/state"] = newValue;
  }
});
 
</syntaxhighlight>
 
== Слежение за контролом == <!--T:20-->


<!--T:21-->
<!--T:21-->
Строка 11: Строка 58:


<!--T:23-->
<!--T:23-->
В примере датчик движения подключен к входу "сухой контакт", контрол типа "switch". Сирена подключена к встроеному реле Wiren Board, а лампа - через релейный блок по Modbus.  Когда вход типа "сухой контакт" (выход датчика движения) замкнут, то на лампу и реле подаётся "1", когда выключен - "0".
В примере датчик движения подключен к входу «сухой контакт», контрол типа «switch». Сирена подключена к встроеному реле Wiren Board, а лампа через релейный блок по Modbus.  Когда вход типа «сухой контакт» (выход датчика движения) замкнут, то на лампу и реле подаётся «1», когда выключен — «0».


<!--T:24-->
<!--T:24-->
Правило срабатывает каждый раз при изменении значения контрола "D1_IN" у устройства "wb-gpio".  В код правила передаётся новое значение этого контрола в виде переменной newValue.
Правило срабатывает каждый раз при изменении значения контрола «D1_IN» у устройства «wb-gpio».  В код правила передаётся новое значение этого контрола в виде переменной newValue.


<!--T:25-->
<!--T:25-->
Строка 20: Строка 67:


<!--T:26-->
<!--T:26-->
defineRule("motion_detector", {
defineRule({
   whenChanged: "wb-gpio/D1_IN",
   whenChanged: "wb-gpio/D1_IN",
   then: function (newValue, devName, cellName) {
   then: function (newValue, devName, cellName) {
dev["wb-gpio"]["Relay_2"] = newValue;
dev["wb-gpio/Relay_2"] = newValue;
dev["wb-mrm2_6"]["Relay 1"] = newValue;
dev["wb-mrm2_6/Relay 1"] = newValue;


   <!--T:27-->
   <!--T:27-->
Строка 52: Строка 99:
   whenChanged: "simple_test/enabled",
   whenChanged: "simple_test/enabled",
   then: function (newValue, devName, cellName) {
   then: function (newValue, devName, cellName) {
dev["wb-gpio"]["Relay_2"] = newValue;
dev["wb-gpio/Relay_2"] = newValue;
dev["wb-mrm2_6"]["Relay 1"] = newValue;
dev["wb-mrm2_6/Relay 1"] = newValue;


   <!--T:32-->
   <!--T:32-->
Строка 60: Строка 107:
</syntaxhighlight>
</syntaxhighlight>


=== Детектор движения c таймаутом === <!--T:34-->
== Мастер-выключатель с восстановлением последнего состояния == <!--T:250-->
 
На вход контроллера подключен мастер-выключатель, который, при переключении, отключает все устройства, указанные в соответствующем правиле. При повторном нажатии на выключатель, устройствам возвращается первоначальное состояние.
 
Подключение осуществляется к контакту A1 и 5V на контроллере. При замыкании на соответствующем канале <code>wb-gpio/A1_IN</code>, состояние меняется, и срабатывает правило.
 
Для управления через веб-интерфейс создано виртуальное устройство, отображаемое на вкладке '''Устройства'''.
 
Первоначальные состояния устройств сохраняются в [https://github.com/wirenboard/wb-rules#%D0%BF%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%BD%D0%BE%D0%B5-%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D0%BB%D0%B8%D1%89%D0%B5 постоянном хранилище]. Переменные в постоянном хранилище записываются на флеш-память, что обеспечивает доступ к ним после перезагрузки контроллера.
 
<syntaxhighlight lang="ecmascript">
defineVirtualDevice("power_off", {
    title: "Мастер-выключатель",
    cells: {
        power_off: {
            type: "pushbutton"
        },
    }
});
 
var ps = new PersistentStorage("power-storage", { global: true });
var lights = ["wb-mdm3_50/K1", "wb-mdm3_50/K2", "wb-mdm3_50/K3"];
 
var isPowerOff = true;
 
defineRule({
    whenChanged: ["wb-gpio/A1_IN", "power_off/power_off"],
    then: function (newValue, devName, cellName) {
        if (isPowerOff) {
            lights.forEach(function (light) {
                ps[light] = dev[light];
                dev[light] = false;
            });
        } else {
            lights.forEach(function (light) {
                dev[light] = ps[light];
            });
        }
        isPowerOff = !isPowerOff;
    }
});
</syntaxhighlight>
 
== Детектор движения c таймаутом == <!--T:34-->


<!--T:35-->
<!--T:35-->
На вход D2 подключен детектор движения с выходом "сухой контакт". При обнаружении движения он замыкает D2 и GND, и на соответствующем канале <code>wb-gpio/D2_IN</code> появляется статус "1".
На вход D2 подключен детектор движения с выходом «сухой контакт». При обнаружении движения он замыкает D2 и GND, и на соответствующем канале <code>wb-gpio/D2_IN</code> появляется статус «1».


<!--T:37-->
<!--T:37-->
Строка 70: Строка 160:
<!--T:36-->
<!--T:36-->
Правило работает так:
Правило работает так:
* когда движение появляется, свет включается. Если ранее был запущен тридцатисекундный таймер "на выключение", этот таймер отключается;
* когда движение появляется, свет включается. Если ранее был запущен тридцатисекундный таймер «на выключение», этот таймер отключается;
* когда движение пропадает, запускается тридцатисекундный таймер "на выключение". Если ему удаётся дойти до конца, свет выключается.
* когда движение пропадает, запускается тридцатисекундный таймер «на выключение». Если ему удаётся дойти до конца, свет выключается.


<!--T:38-->
<!--T:38-->
Строка 78: Строка 168:
var motion_timer_1_id = null;
var motion_timer_1_id = null;


<!--T:191-->
defineRule("motion_detector_1", {
defineRule("motion_detector_1", {
     whenChanged: "wb-gpio/D2_IN",
     whenChanged: "wb-gpio/D2_IN",
     then: function (newValue, devName, cellName) {
     then: function (newValue, devName, cellName) {
         if (newValue) {
         if (newValue) {
             dev["wb-gpio"]["Relay_1"] = true;
             dev["wb-gpio/Relay_1"] = true;
             if (motion_timer_1_id) {
             if (motion_timer_1_id) {
                 clearTimeout(motion_timer_1_id);
                 clearTimeout(motion_timer_1_id);
             }
             }
             motion_timer_1_id = setTimeout(function () {
             motion_timer_1_id = setTimeout(function () {
                 dev["wb-gpio"]["Relay_1"] = false;
                 dev["wb-gpio/Relay_1"] = false;
                 motion_timer_1_id = null;
                 motion_timer_1_id = null;
             }, motion_timer_1_timeout_ms);
             }, motion_timer_1_timeout_ms);
Строка 95: Строка 186:
</syntaxhighlight>
</syntaxhighlight>


=== Создание однотипных правил === <!--T:43-->
== Создание однотипных правил == <!--T:43-->


<!--T:44-->
<!--T:44-->
Строка 108: Строка 199:
       then: function(newValue, devName, cellName) {
       then: function(newValue, devName, cellName) {
           if (!newValue) {
           if (!newValue) {
               dev["wb-gpio"][relay_control] = true;
               dev["wb-gpio/relay_control"] = true;
               if (motion_timer_id) {
               if (motion_timer_id) {
                   clearTimeout(motion_timer_id);
                   clearTimeout(motion_timer_id);
Строка 115: Строка 206:
               <!--T:46-->
               <!--T:46-->
motion_timer_id = setTimeout(function() {
motion_timer_id = setTimeout(function() {
                   dev["wb-gpio"][relay_control] = false;
                   dev["wb-gpio/relay_control"] = false;
                   motion_timer_id = null;
                   motion_timer_id = null;
               }, timeout_ms);
               }, timeout_ms);
Строка 129: Строка 220:
</syntaxhighlight>
</syntaxhighlight>


=== Активация правила только в определённое время === <!--T:48-->
== Активация правила только в определённое время == <!--T:48-->


<!--T:49-->
<!--T:49-->
Строка 160: Строка 251:
     if ((date > date_start) && (date < date_end)) {
     if ((date > date_start) && (date < date_end)) {
       if (newValue) {
       if (newValue) {
           dev["wb-gpio"]["EXT1_R3A1"] = 1;
           dev["wb-gpio/EXT1_R3A1"] = 1;
    
    
           if (motion_timer_1_id) {
           if (motion_timer_1_id) {
Строка 167: Строка 258:
    
    
           motion_timer_1_id = setTimeout(function () {
           motion_timer_1_id = setTimeout(function () {
             dev["wb-gpio"]["EXT1_R3A1"] = 0;             
             dev["wb-gpio/EXT1_R3A1"] = 0;             
             motion_timer_1_id = null;
             motion_timer_1_id = null;
           }, motion_timer_1_timeout_ms);               
           }, motion_timer_1_timeout_ms);               
Строка 176: Строка 267:
</syntaxhighlight>
</syntaxhighlight>


=== Роллеты === <!--T:53-->
== Роллеты == <!--T:53-->


<!--T:54-->
<!--T:54-->
Строка 209: Строка 300:
   defineRule( "roller_shutter_up_on" + suffix, {
   defineRule( "roller_shutter_up_on" + suffix, {
   asSoonAs: function() {
   asSoonAs: function() {
     return dev[relay_up_device][relay_up_control];
     return dev["relay_up_device/relay_up_control"];
   },
   },
     then: function () {
     then: function () {
Строка 218: Строка 309:
       <!--T:59-->
       <!--T:59-->
relay_up_timer_id = setTimeout(function() {
relay_up_timer_id = setTimeout(function() {
         return dev[relay_up_device][relay_up_control] = 0;
         return dev["relay_up_device/relay_up_control"] = 0;
       }, timeout_s * 1000);
       }, timeout_s * 1000);
     }
     }
Строка 226: Строка 317:
defineRule("roller_shutter_down_on" + suffix, {
defineRule("roller_shutter_down_on" + suffix, {
     asSoonAs: function() {
     asSoonAs: function() {
       return dev[relay_down_device][relay_down_control];
       return dev["relay_down_device/relay_down_control"];
     },
     },
     then: function () {
     then: function () {
Строка 234: Строка 325:
        
        
       relay_down_timer_id = setTimeout(function() {
       relay_down_timer_id = setTimeout(function() {
         dev[relay_down_device][relay_down_control] = 0;
         dev["relay_down_device/relay_down_control"] = 0;
       }, timeout_s * 1000);
       }, timeout_s * 1000);
     }
     }
Строка 242: Строка 333:
defineRule("roller_shutter_both_on" + suffix, {
defineRule("roller_shutter_both_on" + suffix, {
     asSoonAs: function() {
     asSoonAs: function() {
       return dev[relay_up_device][relay_up_control] && dev[relay_down_device][relay_down_control];
       return dev["relay_up_device/relay_up_control"] && dev["relay_down_device/relay_down_control"];
     },
     },
     then: function () {
     then: function () {
Строка 257: Строка 348:
       <!--T:63-->
       <!--T:63-->
dev[relay_up_device][relay_up_control] = 0;
dev[relay_up_device][relay_up_control] = 0;
       dev[relay_down_device][relay_down_control] = 0;
       dev["relay_down_device/relay_down_control"] = 0;
       log("Both roller shutter relays on, switching them off");
       log("Both roller shutter relays on, switching them off");
     }
     }
Строка 311: Строка 402:
})();
})();


<!--T:69-->
</syntaxhighlight>
</syntaxhighlight>
== Импульсные счетчики ==
== Импульсные счетчики == <!--T:69-->


<!--T:192-->
Импульсный счетчик подключен к WB-MCM8. Выдает 1 импульс на 10 литров воды. При подключении на счетчике были показания 123.120 м³, что равно 123120 литрам воды. У WB-MCM8 при подключении было насчитано 7 импульсов.
Импульсный счетчик подключен к WB-MCM8. Выдает 1 импульс на 10 литров воды. При подключении на счетчике были показания 123.120 м³, что равно 123120 литрам воды. У WB-MCM8 при подключении было насчитано 7 импульсов.


<!--T:193-->
<syntaxhighlight lang="ecmascript">
<syntaxhighlight lang="ecmascript">
var meterCorrection = 123120 // Корректировочное значение счетчика в литрах
var meterCorrection = 123120 // Корректировочное значение счетчика в литрах
Строка 322: Строка 414:
var inpulseValue = 10 // Количество литров на один импульс
var inpulseValue = 10 // Количество литров на один импульс


<!--T:194-->
defineVirtualDevice("water_meters", { // Создаем виртуальный девайс для отображения в веб интерфейсе.
defineVirtualDevice("water_meters", { // Создаем виртуальный девайс для отображения в веб интерфейсе.
     title: "Счетчики воды",
     title: "Счетчики воды",
Строка 332: Строка 425:
});
});


<!--T:195-->
defineRule("water_meter_1", {
defineRule("water_meter_1", {
     whenChanged: "wb-mcm8_29/Input 1 counter",
     whenChanged: "wb-mcm8_29/Input 1 counter",
     then: function(newValue, devName, cellName) {
     then: function(newValue, devName, cellName) {
       if(newValue){
       if(newValue){
       dev["water_meters"]["water_meter_1"] = ((parseInt(newValue) - counterCorrection) * inpulseValue) + meterCorrection; // Умножаем значение счетчика на количество литров/импульс и прибавляем корректировочное значение.
       dev["water_meters/water_meter_1"] = ((parseInt(newValue) - counterCorrection) * inpulseValue) + meterCorrection; // Умножаем значение счетчика на количество литров/импульс и прибавляем корректировочное значение.
       }
       }
     }
     }
Строка 342: Строка 436:
</syntaxhighlight>
</syntaxhighlight>


== Обработка счётчиков нажатий ==
== Инвертирование значения контрола ==
[[Image:wb-rules-ex-buzzer-invert.png|300px|thumb|right|Пример устройств с вкладки Устройства]]
Правило ниже создаёт виртуальное устройство ''my-invert-buzzer'', с контролом ''disabled'', который инвертирует состояние контрола ''enabled'' системной пищалки ''Buzzer''.
<syntaxhighlight lang="ecmascript">
defineVirtualDevice('my-invert-buzzer', {
    title: 'Buzzer Invert' ,
    cells: {
      Disabled: {
        title: "disabled",
    type: "switch",
    value: !dev["buzzer/enabled"]
    }
    }
})
 
defineRule({
    whenChanged: ["buzzer/enabled"],
    then: function(newValue, devName, cellName) {
      dev["my-invert-buzzer/Disabled"] = !newValue;
    }
});
 
defineRule({
    whenChanged: ["my-invert-buzzer/Disabled"],
    then: function(newValue, devName, cellName){
        dev["buzzer/enabled"] = !newValue;
    }
});
</syntaxhighlight>
 
== Обработка счётчиков нажатий == <!--T:196-->
{{Anchor|press-actions}}
{{Anchor|press-actions}}
=== Описание ===
=== Описание ===
Последние версии прошивок устройств Wiren Board могут распознавать типы нажатий подключённых к входам кнопок и транслировать их по [[Modbus]] на контроллер Wiren Board. О том, как устройство распознаёт типы нажатий, читайте в его документации.  
Последние версии прошивок устройств Wiren Board могут распознавать типы нажатий подключённых к входам кнопок и транслировать их по [[Modbus]] на контроллер Wiren Board. О том, как устройство распознаёт типы нажатий, читайте в его документации.  


<!--T:197-->
Для обработки нажатий нужно отслеживать на контроллере состояние счётчика нужного типа нажатия и, при его изменении, выполнять действие.
Для обработки нажатий нужно отслеживать на контроллере состояние счётчика нужного типа нажатия и, при его изменении, выполнять действие.


<!--T:198-->
Обработку счётчиков удобно делать на [[wb-rules]], но вы можете использовать любой инструмент для автоматизации, например, [[Node-RED]]. Чтобы ускорить опрос счетчиков, настройте [[RS-485:Configuration via Web Interface#poll-period |период опроса]].
Обработку счётчиков удобно делать на [[wb-rules]], но вы можете использовать любой инструмент для автоматизации, например, [[Node-RED]]. Чтобы ускорить опрос счетчиков, настройте [[RS-485:Configuration via Web Interface#poll-period |период опроса]].


=== Примеры ===
=== Примеры === <!--T:199-->
{{YouTube
{{YouTube
|link=https://youtu.be/C60KB7TCeKg
|link=https://youtu.be/C60KB7TCeKg
Строка 362: Строка 488:
# Короткое, а затем длинное — уменьшает яркость.
# Короткое, а затем длинное — уменьшает яркость.


<!--T:200-->
Так как изменение яркости требует растянутое во времени действие, то мы используем таймер. Также мы контролируем состояние входа с кнопкой и прекращаем действие при отпускании кнопки.
Так как изменение яркости требует растянутое во времени действие, то мы используем таймер. Также мы контролируем состояние входа с кнопкой и прекращаем действие при отпускании кнопки.


<!--T:201-->
<syntaxhighlight lang="js">
<syntaxhighlight lang="js">
/* ---------------------------- */
/* ---------------------------- */
Строка 369: Строка 497:
/* ---------------------------- */
/* ---------------------------- */


<!--T:202-->
defineRule({
defineRule({
   whenChanged: "wb-mcm8_20/Input 1 Single Press Counter",
   whenChanged: "wb-mcm8_20/Input 1 Single Press Counter",
Строка 377: Строка 506:




<!--T:203-->
/* ---------------------------- */
/* ---------------------------- */
/* 2. Double Press Counter: Off action*/
/* 2. Double Press Counter: Off action*/
/* ---------------------------- */
/* ---------------------------- */


<!--T:204-->
defineRule({
defineRule({
   whenChanged: "wb-mcm8_20/Input 1 Double Press Counter",
   whenChanged: "wb-mcm8_20/Input 1 Double Press Counter",
Строка 389: Строка 520:




<!--T:205-->
/* --------------------------------------- */
/* --------------------------------------- */
/* 3. Long Press Counter: Increase brightness */
/* 3. Long Press Counter: Increase brightness */
/* --------------------------------------- */
/* --------------------------------------- */


<!--T:206-->
defineRule({
defineRule({
   whenChanged: "wb-mcm8_20/Input 1 Long Press Counter",
   whenChanged: "wb-mcm8_20/Input 1 Long Press Counter",
Строка 401: Строка 534:
});
});


<!--T:207-->
// A rule that will increase the brightness on a timer
// A rule that will increase the brightness on a timer
defineRule({
defineRule({
Строка 407: Строка 541:
     var i = dev["wb-mdm3_58/Channel 1"];
     var i = dev["wb-mdm3_58/Channel 1"];


     if (i < 100 && dev["wb-mcm8_20/Input 1"]) {
     <!--T:208-->
if (i < 100 && dev["wb-mcm8_20/Input 1"]) {
       i++
       i++
       dev["wb-mdm3_58/Channel 1"] = i
       dev["wb-mdm3_58/Channel 1"] = i
Строка 417: Строка 552:




<!--T:209-->
/* -------------------------------------------- */
/* -------------------------------------------- */
/* 4. Shortlong Press Counter: Decrease brightness */
/* 4. Shortlong Press Counter: Decrease brightness */
/* -------------------------------------------- */
/* -------------------------------------------- */


<!--T:210-->
defineRule({
defineRule({
   whenChanged: "wb-mcm8_20/Input 1 Shortlong Press Counter",
   whenChanged: "wb-mcm8_20/Input 1 Shortlong Press Counter",
Строка 429: Строка 566:
});
});


<!--T:211-->
// A rule that will decrease the brightness on a timer
// A rule that will decrease the brightness on a timer
defineRule({
defineRule({
Строка 435: Строка 573:
     var i = dev["wb-mdm3_58/Channel 1"];
     var i = dev["wb-mdm3_58/Channel 1"];


     if (i > 0 && dev["wb-mcm8_20/Input 1"]) {
     <!--T:212-->
if (i > 0 && dev["wb-mcm8_20/Input 1"]) {
       i--
       i--
       dev["wb-mdm3_58/Channel 1"] = i
       dev["wb-mdm3_58/Channel 1"] = i
Строка 444: Строка 583:
});
});


<!--T:213-->
</syntaxhighlight>
</syntaxhighlight>


=== Универсальный модуль для wb-rules ===
=== Универсальный модуль для wb-rules === <!--T:214-->
Мы написали модуль для wb-rules [https://github.com/wirenboard/wb-community/tree/main/wb-press-actions wb-press-actions], который облегчает обработку нажатий в ваших скриптах.
Мы написали модуль для wb-rules [https://github.com/wirenboard/wb-community/tree/main/scripts/wb-press-actions wb-press-actions], который облегчает обработку нажатий в ваших скриптах.


== Датчик MSW v.3 ==
== Датчик MSW v.3 == <!--T:215-->


<!--T:216-->
При подключении датчика WB-MSW v.3 к контроллеру Wiren Board есть возможность создавать интересные сценарии, используя данные с датчика. На пример Включать свет по движению, сигнализировать светодиодами о превышении значения СО2 или VOC, Включать Кондиционер, если жарко или увлажнитель воздуха, если воздух слишком сухой. Правила создаются индивидуально под задачи. Здесь мы приведем несколько примеров для понимания принципа работы с датчиком. Больше примеров написания правил можно найти в документации '''[[Движок правил wb-rules]]'''.
При подключении датчика WB-MSW v.3 к контроллеру Wiren Board есть возможность создавать интересные сценарии, используя данные с датчика. На пример Включать свет по движению, сигнализировать светодиодами о превышении значения СО2 или VOC, Включать Кондиционер, если жарко или увлажнитель воздуха, если воздух слишком сухой. Правила создаются индивидуально под задачи. Здесь мы приведем несколько примеров для понимания принципа работы с датчиком. Больше примеров написания правил можно найти в документации '''[[Движок правил wb-rules]]'''.


=== CO2 ===
=== CO2 === <!--T:217-->
При концентрации CO2 меньше 650 - раз в 10 секунд мигаем зеленым.
При концентрации CO2 меньше 650 - раз в 10 секунд мигаем зеленым.


<!--T:218-->
При концентрации CO2 свыше 651, но меньше 1000 - раз в 5 секунд мигаем желтым.
При концентрации CO2 свыше 651, но меньше 1000 - раз в 5 секунд мигаем желтым.


<!--T:219-->
При концентрации CO2 свыше 1001 - раз в секунду мигаем красным.
При концентрации CO2 свыше 1001 - раз в секунду мигаем красным.
<div class="mw-collapsible mw-collapsed"; style="width:600px; overflow: hidden;">
<div class="mw-collapsible mw-collapsed"; style="width:600px; overflow: hidden;">
Строка 469: Строка 612:


         if (co2_good) {
         if (co2_good) {
             dev[devName]["Green LED"] = true;
             dev[devName+"/Green LED"] = true;
             dev[devName]["Red LED"] = false;
             dev[devName+"/Red LED"] = false;
             dev[devName]["LED Period (s)"] = 10;
             dev[devName+"/LED Period (s)"] = 10;
         }
         }
         if (co2_middle) {
         if (co2_middle) {
             dev[devName]["Green LED"] = true;
             dev[devName+"/Green LED"] = true;
             dev[devName]["Red LED"] = true;
             dev[devName+"/Red LED"] = true;
             dev[devName]["LED Period (s)"] = 5;
             dev[devName+"/LED Period (s)"] = 5;
         }
         }
         if (co2_bad) {
         if (co2_bad) {
             dev[devName]["Green LED"] = false;
             dev[devName+"/Green LED"] = false;
             dev[devName]["Red LED"] = true;
             dev[devName+"/Red LED"] = true;
             dev[devName]["LED Period (s)"] = 1;
             dev[devName+"/LED Period (s)"] = 1;
         }
         }
     }
     }
});
});
</syntaxhighlight>
</syntaxhighlight>
Но когда устройств/правил много их целесообразно создавать одной функцией, передавая в нее разные параметры:
<syntaxhighlight lang="ecmascript">
function ruleCO2 (devCO2, minCO2, maxCO2){
  log.debug("rule create", devCO2)
  defineRule ("ruleCO2"+devCO2, {
      whenChanged: devCO2+"/CO2",
      then: function(newValue, devName, cellName) {
        log.info("ruleCO2 " + devCO2 +" enter with", newValue)
        if (newValue < minCO2) {
          dev[devCO2+"/LED Glow Duration (ms)"] = 50;
          dev[devCO2+"/Green LED"] = true;
          dev[devCO2+"/Red LED"] = false;
          dev[devCO2+"/LED Period (s)"] = 3;
        }
        if ((newValue > minCO2) && (newValue < maxCO2)) {
          dev[devCO2+"/LED Glow Duration (ms)"] = 50;           
          dev[devCO2+"/Green LED"] = true;
          dev[devCO2+"/Red LED"] = true;
          dev[devCO2+"/LED Period (s)"] = 2;
        }
        if (newValue > maxCO2) {
          dev[devCO2+"/LED Glow Duration (ms)"] = 50;           
          dev[devCO2+"/Green LED"] = false;
          dev[devCO2+"/Red LED"] = true;
          dev[devCO2+"/LED Period (s)"] = 1;
        }
      }
  });
}
ruleCO2("wb-msw-v3_97", 650, 1000);
ruleCO2("wb-msw-v3_98", 650, 1000);
ruleCO2("wb-msw-v3_11", 500, 700);
</syntaxhighlight>
</div>
</div>
=== Max Motion ===
 
=== Max Motion === <!--T:220-->
"Max Motion" - максимальное значение датчика движения за N время. Время от 1 до 60 секунд можно выставить в 282 регистре. По умолчанию 10 секунд. При достижении Max Motion значения 50 проверяем достаточно ли освещена комната, если нет - включаем свет. Как только значение Max Motion упадет ниже 50 свет выключаем.
"Max Motion" - максимальное значение датчика движения за N время. Время от 1 до 60 секунд можно выставить в 282 регистре. По умолчанию 10 секунд. При достижении Max Motion значения 50 проверяем достаточно ли освещена комната, если нет - включаем свет. Как только значение Max Motion упадет ниже 50 свет выключаем.


<!--T:221-->
<div class="NavFrame">
<div class="NavFrame">
   <div class="NavContent">
   <div class="NavContent">
Строка 497: Строка 680:
     then: function(newValue, devName, cellName) {
     then: function(newValue, devName, cellName) {
         if (newValue > 50) {
         if (newValue > 50) {
             if (dev["wb-msw-v3_97"]["Illuminance"] < 50) {
             if (dev["wb-msw-v3_97/Illuminance"] < 50) {
                 dev["wb-mr3_11"]["K1"] = true;
                 dev["wb-mr3_11/K1"] = true;
             }
             }
         } else {
         } else {
             dev["wb-mr3_11"]["K1"] = false;
             dev["wb-mr3_11/K1"] = false;
         }
         }
     }
     }
Строка 511: Строка 694:
== Системные правила == <!--T:70-->
== Системные правила == <!--T:70-->


<!--T:222-->
Многие показания, которые видны в веб-интерфейсе контроллера из коробки, тоже создаются правилами на движке wb-rules. Их код находится здесь: [https://github.com/wirenboard/wb-rules-system https://github.com/wirenboard/wb-rules-system]. Системные правила собраны в пакет <code>wb-rules-system</code>, сами файлы скриптов на контроллере находятся в папке <code>/usr/share/wb-rules-system/</code>.  
Многие показания, которые видны в веб-интерфейсе контроллера из коробки, тоже создаются правилами на движке wb-rules. Их код находится здесь: [https://github.com/wirenboard/wb-rules-system https://github.com/wirenboard/wb-rules-system]. Системные правила собраны в пакет <code>wb-rules-system</code>, сами файлы скриптов на контроллере находятся в папке <code>/usr/share/wb-rules-system/</code>.  


Строка 642: Строка 826:
     whenChanged: "wb-adc/Vin",
     whenChanged: "wb-adc/Vin",
     then: function() {
     then: function() {
         if (dev["wb-adc"]["Vin"] < dev["wb-adc"]["BAT"] ) {
         if (dev["wb-adc"]["Vin"] < dev["wb-adc/BAT"] ) {
             dev["power_status"]["Vin"] = 0;
             dev["power_status/Vin"] = 0;
         } else {
         } else {
             dev["power_status"]["Vin"] = dev["wb-adc"]["Vin"] ;
             dev["power_status/Vin"] = dev["wb-adc/Vin"] ;
         }
         }
     }
     }
Строка 655: Строка 839:
defineRule("_system_dc_on", {
defineRule("_system_dc_on", {
   asSoonAs: function () {
   asSoonAs: function () {
     return  dev["wb-adc"]["Vin"] > dev["wb-adc"]["BAT"];
     return  dev["wb-adc/Vin"] > dev["wb-adc/BAT"];
   },
   },
   then: function () {
   then: function () {
     dev["power_status"]["working on battery"] = false;
     dev["power_status/working on battery"] = false;
   }
   }
});
});
Строка 665: Строка 849:
defineRule("_system_dc_off", {
defineRule("_system_dc_off", {
   asSoonAs: function () {
   asSoonAs: function () {
     return  dev["wb-adc"]["Vin"] <= dev["wb-adc"]["BAT"];
     return  dev["wb-adc/Vin"] <= dev["wb-adc/BAT"];
   },
   },
   then: function () {
   then: function () {
     dev["power_status"]["working on battery"] = true;
     dev["power_status/working on battery"] = true;
   }
   }
});
});


<!--T:98-->
<!--T:98-->
</syntaxhighlight>
== Термостат == <!--T:223-->
Пример простого термостата из [https://support.wirenboard.com/t/novaya-versiya-dvizhka-pravil/4196/158 темы на портале поддержки].
<!--T:224-->
<syntaxhighlight lang="js">
defineVirtualDevice("Termostat", {
    title: "Termostat",
    cells: {
      // =============== Прихожая теплый пол
      "R01-TS16-1-mode": {//режим 0-ручной 1-по расписанию
    type: "switch",
    value: false,
    },
      "R01-TS16-1-setpoint": {//уставка
    type: "range",
    value: 25,
        max: 30,
        readonly: false
    },
      "R01-TS16-1-lock": {//блокировка в визуализации 0-снята 1-заблокирована
    type: "switch",
    value: false,
    },
  }
})
<!--T:225-->
var hysteresis = 0.5;
function Termostat(name, temp, setpoint, TS, TS_onoff) {
defineRule(name, {
  whenChanged: temp, //при изменении состояния датчика
  then: function (newValue, devName, cellName) { //выполняй следующие действия
    if (dev[TS_onoff]) {
    if ( newValue < dev[setpoint] - hysteresis) { //если температура датчика меньше уставки - гистерезис
      dev[TS] = true;
    }
    if ( newValue > dev[setpoint] + hysteresis) { //если температура датчика больше виртуальной уставки + гистерезис
      dev[TS] = false;
    }
    }
    else dev[TS] = false;
  }
});
}
<!--T:226-->
Termostat("R01-TS16-1", "A60-M1W3/External Sensor 1", "Termostat/R01-TS16-1-setpoint", "wb-gpio/EXT4_R3A1", "Termostat/R01-TS16-1-onoff"); // Прихожая теплый пол
</syntaxhighlight>
</syntaxhighlight>


Строка 728: Строка 964:
<!--T:112-->
<!--T:112-->
<pre>
<pre>
root@wirenboard:~# mcedit /etc/wb-rules/rs485_cmd.js
nano /etc/wb-rules/rs485_cmd.js
</pre>
</pre>


Строка 752: Строка 988:
<!--T:116-->
<!--T:116-->
<pre>
<pre>
root@wirenboard:~# /etc/init.d/wb-rules restart
systemctl restart wb-rules
root@wirenboard:~# tail -f /var/log/messages
</pre>
<pre>
journalctl -u wb-rules -f
</pre>
</pre>


Строка 853: Строка 1091:
</syntaxhighlight>
</syntaxhighlight>


== Отправка сообщения через Telegram-бота ==
== Отправка сообщения через Telegram-бота == <!--T:227-->
{{Anchor|telegram}}
{{Anchor|telegram}}
Сообщения отправляются с использованием [https://core.telegram.org/api#telegram-api Telegram API] через <code>curl</code>.
Сообщения отправляются с использованием [https://core.telegram.org/api#telegram-api Telegram API] через <code>curl</code>.


<!--T:228-->
<syntaxhighlight lang="ecmascript">
<syntaxhighlight lang="ecmascript">
var message = "Text"; // напишите свой текст сообщения
var message = "Text"; // напишите свой текст сообщения
Строка 863: Строка 1102:
var command = 'curl -s -X POST https://api.telegram.org/bot{}/sendMessage -d chat_id={} -d text="{}"'.format(token, chat_id, message);
var command = 'curl -s -X POST https://api.telegram.org/bot{}/sendMessage -d chat_id={} -d text="{}"'.format(token, chat_id, message);


<!--T:229-->
runShellCommand(command);
runShellCommand(command);
</syntaxhighlight>
</syntaxhighlight>


== Обработка ошибок в работе с serial-устройствами ==
== Обработка ошибок в работе с serial-устройствами == <!--T:230-->
Реализована через подписку на все топики '''meta/error'''.
Реализована через подписку на все топики '''meta/error'''.


<!--T:231-->
<syntaxhighlight lang="ecmascript">
<syntaxhighlight lang="ecmascript">
defineVirtualDevice("meta_error_test", {
defineVirtualDevice("meta_error_test", {
Строка 887: Строка 1128:


   
   
<!--T:232-->
trackMqtt("/devices/+/controls/+/meta/error", function(message){
trackMqtt("/devices/+/controls/+/meta/error", function(message){
   log.info("name: {}, value: {}".format(message.topic, message.value))
   log.info("name: {}, value: {}".format(message.topic, message.value))
Строка 900: Строка 1142:




<!--T:233-->
[[File:Sample-custom-config-1.png|300px|thumb|right|Пример конфигурации]]
[[File:Sample-custom-config-1.png|300px|thumb|right|Пример конфигурации]]
[[File:Sample-custom-config-2.png|300px|thumb|right|Пример скрипта]]
[[File:Sample-custom-config-2.png|300px|thumb|right|Пример скрипта]]
Строка 908: Строка 1151:
Но сложную настройку с меню и вариантами так не сделать.
Но сложную настройку с меню и вариантами так не сделать.


<!--T:234-->
Правильный, но сложный способ — создать новую вкладку в разделе Configs с редактируемыми полями параметров установок .
Правильный, но сложный способ — создать новую вкладку в разделе Configs с редактируемыми полями параметров установок .


<!--T:235-->
Потребуются три файла:
Потребуются три файла:


<!--T:236-->
1. Схема вывода html странички в разделе Configs : /usr/share/wb-mqtt-confed/schemas/test-config.schema.json
1. Схема вывода html странички в разделе Configs : /usr/share/wb-mqtt-confed/schemas/test-config.schema.json
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Строка 919: Строка 1165:
"description":"Long description configuration",
"description":"Long description configuration",


<!--T:237-->
"configFile": {
"configFile": {
"path":"/etc/test-config.conf",
"path":"/etc/test-config.conf",
Строка 924: Строка 1171:
},
},


<!--T:238-->
"properties": {
"properties": {
"temperature_setpoint": {
"temperature_setpoint": {
Строка 934: Строка 1182:
},
},


<!--T:239-->
"humidity_setpoint": {
"humidity_setpoint": {
"type":"number",
"type":"number",
Строка 947: Строка 1196:
</syntaxhighlight>
</syntaxhighlight>


<!--T:240-->
2. Описание конфигурации по умолчанию (при сохранении формы в веб интерфейсе, значения запишутся в этот файл) : /etc/test-config.conf
2. Описание конфигурации по умолчанию (при сохранении формы в веб интерфейсе, значения запишутся в этот файл) : /etc/test-config.conf
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
{
{
"temperature_setpoint": 60,
"temperature_setpoint": 25,
"humidity_setpoint": 14
"humidity_setpoint": 14
}
}
</syntaxhighlight>
</syntaxhighlight>


<!--T:241-->
3. Скрипт, обновляющий конфиг : /mnt/data/etc/wb-rules/test-config-script.js
3. Скрипт, обновляющий конфиг : /mnt/data/etc/wb-rules/test-config-script.js


<!--T:242-->
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
var config = readConfig("/etc/test-config.conf");
var config = readConfig("/etc/test-config.conf");


<!--T:243-->
log("temperature setpoint is: {}".format(config.temperature_setpoint));
log("temperature setpoint is: {}".format(config.temperature_setpoint));
</syntaxhighlight>
</syntaxhighlight>


<!--T:244-->
Последний файл можно в том числе редактировать из веб-интерфейса на вкладке Scripts.
Последний файл можно в том числе редактировать из веб-интерфейса на вкладке Scripts.


<!--T:245-->
В json файлах описаны схемы вывода html странички браузером, по общепринятому стандарту отображения. Описание ключей тут: json-schema.org.
В json файлах описаны схемы вывода html странички браузером, по общепринятому стандарту отображения. Описание ключей тут: json-schema.org.


<!--T:246-->
После создания файлов, нужно выполнить рестарт сервисов
После создания файлов, нужно выполнить рестарт сервисов


<!--T:247-->
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
service wb-mqtt-confed restart
service wb-mqtt-confed restart


<!--T:248-->
service wb-rules restart
service wb-rules restart
</syntaxhighlight>
</syntaxhighlight>


<!--T:249-->
При нажатии кнопки Save в веб-интерфейсе, будет перезапускаться сервис wb-rules, а значения установок - записываться в правила.
При нажатии кнопки Save в веб-интерфейсе, будет перезапускаться сервис wb-rules, а значения установок - записываться в правила.


Строка 986: Строка 1245:


<!--T:141-->
<!--T:141-->
Например, мы хотим, чтобы освещение было включено с 10 до 17ч. Обёртка (libschedule) будет выполнять правило "включить освещение" раз в минуту с 10 утра до 17 вечера.
Например, мы хотим, чтобы освещение было включено с 10 до 17ч. Обёртка (libschedule) будет выполнять правило «включить освещение» раз в минуту с 10 утра до 17 вечера.


<!--T:142-->
<!--T:142-->
Строка 1398: Строка 1657:
   });
   });


   <!--T:190-->
   defineRule("acVegOff", {
defineRule("acVegOff", {
     when: function() {
     when: function() {
       return vegetablesTemperature < 17.8
       return vegetablesTemperature < 17.8
Строка 1410: Строка 1668:
})()
})()
</syntaxhighlight>
</syntaxhighlight>
== Полезные ссылки ==
 
==Работа с JSON==
Движок wb-rules поддерживает стандартные функции языка JavaScript для работы с JSON:
 
*<code>JSON.stringify()</code> — преобразует объект в JSON-строку;
*<code>JSON.parse()</code> — преобразует JSON-строку в объект.
 
Более подробную информацию о функциях можно найти в учебнике [https://learn.javascript.ru/json JavaScript].
 
Эти функции требуются, когда вы получаете данные из другого сервиса в JSON-формате.
 
В приведенном примере создается виртуальное устройство с одной кнопкой и числовым параметром, который который хранится в виде JSON-строки. При нажатии на кнопку к значению параметра прибавляется 1.
<syntaxhighlight lang="bash">
defineVirtualDevice("JSON_test", {
    title: "JSON_device",
    cells: {
Button: {
    type: "pushbutton",
    value: false
},
    Json: {
        type : "text",
        value : JSON.stringify({param: 0}),
    },     
  }
});
 
defineRule("change_value", {
  whenChanged: "JSON_test/Button",
  then: function () {
    parameter = JSON.parse(dev["JSON_test/Json"]);
    parameter.param++;
    dev["JSON_test/Json"] = JSON.stringify(parameter)
    }
});
</syntaxhighlight>
 
 
==Работа с последовательным портом через RPC==
[[File:mqtt-rpc.png|300px|thumb|right|Работа с последовательным портом через RPC]]
Если устройство на шине работает по протоколу, который не поддерживается драйвером [[Wb-mqtt-serial_driver |wb-mqtt-serial]] можно формировать запросы вручную и отправлять их драйверу через [https://github.com/wirenboard/mqtt-rpc RPC-MQTT].
 
RPC-MQTT создает MQTT-топик для отправки запросов, и топик для чтения ответов от драйвера. Поэтому для его использования достаточно отправить запрос в нужный топик функцией <code>publish()</code> и прочитать ответ функцией <code>trackMqtt()</code>. Как узнать адреса топиков описано в [https://github.com/wirenboard/mqtt-rpc документации].
 
В примере написан скрипт на wb-rules для отправки Modbus-запроса устройству Wiren Board на шине RS-485.
 
Переменная <code>message</code> содержит Modbus-запрос, сформированный в соответствии со стандартом [[Modbus |Modbus RTU]].
 
Переменная <code>pathRPC</code> — это адрес MQTT-топика, в который отправляются запросы для драйвера wb-mqtt-serial. Для каждого сервиса используется свой топик, и узнать его адрес можно из документации на RPC-MQTT.
 
<syntaxhighlight lang="bash">
var pathRPC = "/rpc/v1/wb-mqtt-serial/port/Load/";  //Адрес топика в который отправляется запрос
var modbusPort = "/dev/ttyRS485-1";
var modbusSpeed = 9600;
var modbusParity = "N";
var modbusStopbit = 2;
var message = "E0300C8000644C9";
 
 
var clientID = "testRPC";
 
 
function requestRPC(modbusPort, modbusSpeed, modbusParity, modbusStopbit, clientID, requiestID, messageType, message, responseSize){
var strJson = JSON.stringify({params: {response_size: responseSize, format: messageType, path: modbusPort, baud_rate: modbusSpeed, parity: modbusParity, "data_bits" : 8, "stop_bits" : modbusStopbit, "msg": message}, "id" : requiestID});
  log.info("strJson =", strJson);
 
  publish(pathRPC+clientID, strJson, 2, false)
};
 
 
trackMqtt(pathRPC+clientID+"/reply", function(message){
  log.info("name: {}, value: {}".format(message.topic, message.value))
});
 
 
requestRPC(modbusPort, modbusSpeed, modbusParity, modbusStopbit, clientID, 1, "HEX", message, 8)
 
</syntaxhighlight>
 
Если запрос отправлен без ошибок, то в лог будет выведено сообщение вида:
<syntaxhighlight lang="bash">
name: /rpc/v1/wb-mqtt-serial/port/Load/testRPC/reply,
value: {"error":null,"id":1,"result":{"response":"0e030400002569df"}}
</syntaxhighlight>
 
== Получение SMS ==
В примере с периодом в 1 секунду выводится в лог вся информация о последнем сообщении. Полученные SMS будут в capturedOutput. Пример из [https://support.wirenboard.com/t/wb7-modem-rabota-s-sms-soobshheniyami/18159 темы на портале].
 
<syntaxhighlight lang="bash">
var period = 1000;
setInterval(function() {
    runShellCommand("mmcli --modem wbc --messaging-list-sms --output-keyvalue | grep length | cut -f2 -d':'", {
        captureOutput: true,
        exitCallback: function(exitCode, capturedOutput) {
            if (exitCode === 0) {
                runShellCommand("mmcli --modem wbc --sms " + (parseInt(capturedOutput) - 1).toString(), {
                    captureOutput: true,
                    exitCallback: function(exitCode, capturedOutput) {
                        if (exitCode === 0) {
                            log(capturedOutput);
                            return;
                        }
                    }
                });
                return;
            }
        }
    });
}, period);
</syntaxhighlight>
 
== Полезные ссылки == <!--T:190-->
* [[Wb-rules | Краткое описание wb-rules на wiki]]
* [[Wb-rules | Краткое описание wb-rules на wiki]]
* [https://github.com/wirenboard/wb-rules Полное описание wb-rules на Github]
* [https://github.com/wirenboard/wb-rules Полное описание wb-rules на Github]
</translate>
</translate>