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

12 901 байт добавлено ,  17 дней назад
 
(не показано 29 промежуточных версий 6 участников)
Строка 1: Строка 1:
<languages/>
<languages/>
<translate>
<translate>
<!--T:250-->
<!--T:255-->
{{DISPLAYTITLE: Примеры правил}}
{{DISPLAYTITLE: Примеры правил}}


=== Слежение за контролом === <!--T:20-->
== Общая информация ==
Здесь вы найдёте учебные примеры скриптов, написанных для движка правил '''[[wb-rules| wb-rules]]'''.
 
Алгоритмы в примерах предельно просты и не учитывают многих факторов которые могут возникнуть в реальности. Поэтому используйте эту библиотеку только как учебный материал, а не источник готовых скриптов для реальных проектов.
 
== Виртуальное устройство ==
 
Виртуальное устройство можно использовать для объединения каналов, задания особой логики для устройства или просто так для красоты.
 
Пример ниже создаст виртуальное устройство с именем '''deviceName''' и двумя контролами '''value''' и '''state'''. А благодаря правилу с '''whenChanged''', значение контрола '''state''' будет менять в зависимости от значение контрола '''value'''.
 
Виртуальным устройствам и контролам можно присваивать русские имена, задавая '''title''' в виде <code>title: { en: ’Title’, ru: ’Заголовок’ }</code>, или через '''setTitle''' у контрола: <code>setTitle({ en: ’Title’, ru: ’Заголовок’ })</code>.
 
Для значений параметров с типом '''value''' и '''text''' можно использовать перечисления '''enum''' в виде набора именованных констант. Перечисления удобно использовать, когда значение параметра может принимать ограниченное количество значений, например, дни недели.
 
Чтобы задать перечисление используйте для нужного контрола параметр '''enum''' с набором пар <code>“ключ”: “значение”</code>.
 
Если параметр имеет тип '''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-->
Строка 13: Строка 67:


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


<!--T:24-->
<!--T:24-->
Строка 55: Строка 109:
   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-->
Строка 62: Строка 116:
</syntaxhighlight>
</syntaxhighlight>


=== Детектор движения c таймаутом === <!--T:34-->
== Мастер-выключатель с восстановлением последнего состояния == <!--T:250-->
 
На вход контроллера подключен мастер-выключатель, который, при переключении, отключает все устройства, указанные в соответствующем правиле. При повторном нажатии на выключатель, устройствам возвращается первоначальное состояние.


<!--T:35-->
Подключение осуществляется к контакту A1 и 5V на контроллере. При замыкании на соответствующем канале <code>wb-gpio/A1_IN</code>, состояние меняется, и срабатывает правило.
На вход D2 подключен детектор движения с выходом «сухой контакт». При обнаружении движения он замыкает D2 и GND, и на соответствующем канале <code>wb-gpio/D2_IN</code> появляется статус «1».
 
Для управления через веб-интерфейс создано виртуальное устройство, отображаемое на вкладке '''Устройства'''.
 
Первоначальные состояния устройств сохраняются в [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-->
На вход D2 подключен детектор движения с выходом «сухой контакт». При обнаружении движения он замыкает D2 и GND, и на соответствующем канале <code>wb-gpio/D2_IN</code> появляется статус «1».


<!--T:37-->
<!--T:37-->
Строка 98: Строка 195:
</syntaxhighlight>
</syntaxhighlight>


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


<!--T:44-->
<!--T:44-->
Строка 132: Строка 229:
</syntaxhighlight>
</syntaxhighlight>


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


<!--T:49-->
<!--T:49-->
Строка 179: Строка 276:
</syntaxhighlight>
</syntaxhighlight>


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


<!--T:54-->
<!--T:54-->
Строка 348: Строка 445:
</syntaxhighlight>
</syntaxhighlight>


== Обработка счётчиков нажатий == <!--T:196-->
== Инвертирование значения контрола ==
{{Anchor|press-actions}}
[[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}}
=== Описание ===
=== Описание ===
Последние версии прошивок устройств Wiren Board могут распознавать типы нажатий подключённых к входам кнопок и транслировать их по [[Modbus]] на контроллер Wiren Board. О том, как устройство распознаёт типы нажатий, читайте в его документации.  
Последние версии прошивок устройств Wiren Board могут распознавать типы нажатий подключённых к входам кнопок и транслировать их по [[Modbus]] на контроллер Wiren Board. О том, как устройство распознаёт типы нажатий, читайте в его документации.  
Строка 469: Строка 596:


=== Универсальный модуль для wb-rules === <!--T:214-->
=== Универсальный модуль для 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 == <!--T:215-->
== Датчик MSW v.3 == <!--T:215-->
Строка 494: Строка 621:


         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;
         }
         }
     }
     }
Строка 763: Строка 890:
    type: "switch",
    type: "switch",
    value: false,
    value: false,
    },.......
    },
  }
})


<!--T:225-->
<!--T:225-->
Строка 790: Строка 919:


</syntaxhighlight>
</syntaxhighlight>
== Отправка команд по RS-485 == <!--T:99-->
== Отправка команд по RS-485 == <!--T:99-->


Строка 1079: Строка 1209:
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
{
{
"temperature_setpoint": 60,
"temperature_setpoint": 25,
"humidity_setpoint": 14
"humidity_setpoint": 14
}
}
Строка 1114: Строка 1244:
<!--T:249-->
<!--T:249-->
При нажатии кнопки Save в веб-интерфейсе, будет перезапускаться сервис wb-rules, а значения установок - записываться в правила.
При нажатии кнопки Save в веб-интерфейсе, будет перезапускаться сервис wb-rules, а значения установок - записываться в правила.
Работает всё это через [https://github.com/wirenboard/json-editor json-editor].


== Сложные правила с расписаниями == <!--T:138-->
== Сложные правила с расписаниями == <!--T:138-->
Строка 1547: Строка 1679:
})()
})()
</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-->
== Полезные ссылки == <!--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>
translator, wb_editors
4285

правок