16 885
правок
Matveevrj (обсуждение | вклад) |
|||
(не показана 31 промежуточная версия 6 участников) | |||
Строка 1: | Строка 1: | ||
<languages/> | <languages/> | ||
<translate> | <translate> | ||
=== Слежение за контролом | <!--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, а лампа | В примере датчик движения подключен к входу «сухой контакт», контрол типа «switch». Сирена подключена к встроеному реле Wiren Board, а лампа — через релейный блок по Modbus. Когда вход типа «сухой контакт» (выход датчика движения) замкнут, то на лампу и реле подаётся «1», когда выключен — «0». | ||
<!--T:24--> | <!--T:24--> | ||
Строка 53: | Строка 100: | ||
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/ | dev["wb-mrm2_6/Relay 1"] = newValue; | ||
<!--T:32--> | <!--T:32--> | ||
Строка 60: | Строка 107: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | == Мастер-выключатель с восстановлением последнего состояния == <!--T:250--> | ||
На вход контроллера подключен мастер-выключатель, который, при переключении, отключает все устройства, указанные в соответствующем правиле. При повторном нажатии на выключатель, устройствам возвращается первоначальное состояние. | |||
На вход | |||
Подключение осуществляется к контакту A1 и 5V на контроллере. При замыкании на соответствующем канале <code>wb-gpio/A1_IN</code>, состояние меняется, и срабатывает правило. | |||
<!--T:37--> | Для управления через веб-интерфейс создано виртуальное устройство, отображаемое на вкладке '''Устройства'''. | ||
Освещение подключено через встроенное реле, соответствующий канал <code>wb-gpio/Relay_1</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--> | |||
На вход D2 подключен детектор движения с выходом «сухой контакт». При обнаружении движения он замыкает D2 и GND, и на соответствующем канале <code>wb-gpio/D2_IN</code> появляется статус «1». | |||
<!--T:37--> | |||
Освещение подключено через встроенное реле, соответствующий канал <code>wb-gpio/Relay_1</code>. | |||
<!--T:36--> | <!--T:36--> | ||
Строка 96: | Строка 186: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Создание однотипных правил == <!--T:43--> | |||
<!--T:44--> | <!--T:44--> | ||
Строка 130: | Строка 220: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Активация правила только в определённое время == <!--T:48--> | |||
<!--T:49--> | <!--T:49--> | ||
Строка 177: | Строка 267: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Роллеты == <!--T:53--> | |||
<!--T:54--> | <!--T:54--> | ||
Строка 346: | Строка 436: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Обработка счётчиков нажатий == <!--T:196--> | == Инвертирование значения контрола == | ||
[[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}} | ||
=== Описание === | === Описание === | ||
Строка 467: | Строка 587: | ||
=== Универсальный модуль для 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--> | ||
Строка 492: | Строка 612: | ||
if (co2_good) { | if (co2_good) { | ||
dev[" | dev[devName+"/Green LED"] = true; | ||
dev[" | dev[devName+"/Red LED"] = false; | ||
dev[" | dev[devName+"/LED Period (s)"] = 10; | ||
} | } | ||
if (co2_middle) { | if (co2_middle) { | ||
dev[" | dev[devName+"/Green LED"] = true; | ||
dev[" | dev[devName+"/Red LED"] = true; | ||
dev[" | dev[devName+"/LED Period (s)"] = 5; | ||
} | } | ||
if (co2_bad) { | if (co2_bad) { | ||
dev[" | dev[devName+"/Green LED"] = false; | ||
dev[" | dev[devName+"/Red LED"] = true; | ||
dev[" | dev[devName+"/LED Period (s)"] = 1; | ||
} | } | ||
} | } | ||
}); | }); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Но когда устройств/правил много их целесообразно создавать одной функцией, передавая в нее разные параметры: | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
defineRule("msw3_Motion", { | function ruleCO2 (devCO2, minCO2, maxCO2){ | ||
whenChanged: "wb-msw-v3_97/Max Motion", | log.debug("rule create", devCO2) | ||
then: function(newValue, devName, cellName) { | 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> | |||
=== Max Motion === <!--T:220--> | |||
"Max Motion" - максимальное значение датчика движения за N время. Время от 1 до 60 секунд можно выставить в 282 регистре. По умолчанию 10 секунд. При достижении Max Motion значения 50 проверяем достаточно ли освещена комната, если нет - включаем свет. Как только значение Max Motion упадет ниже 50 свет выключаем. | |||
<!--T:221--> | |||
<div class="NavFrame"> | |||
<div class="NavContent"> | |||
<syntaxhighlight lang="ecmascript"> | |||
defineRule("msw3_Motion", { | |||
whenChanged: "wb-msw-v3_97/Max Motion", | |||
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) { | ||
Строка 722: | Строка 881: | ||
type: "switch", | type: "switch", | ||
value: false, | value: false, | ||
}, | }, | ||
} | |||
}) | |||
<!--T:225--> | <!--T:225--> | ||
Строка 749: | Строка 910: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Отправка команд по RS-485 == <!--T:99--> | == Отправка команд по RS-485 == <!--T:99--> | ||
Строка 802: | Строка 964: | ||
<!--T:112--> | <!--T:112--> | ||
<pre> | <pre> | ||
nano /etc/wb-rules/rs485_cmd.js | |||
</pre> | </pre> | ||
Строка 826: | Строка 988: | ||
<!--T:116--> | <!--T:116--> | ||
<pre> | <pre> | ||
systemctl restart wb-rules | |||
</pre> | |||
<pre> | |||
journalctl -u wb-rules -f | |||
</pre> | </pre> | ||
Строка 1036: | Строка 1200: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
{ | { | ||
"temperature_setpoint": | "temperature_setpoint": 25, | ||
"humidity_setpoint": 14 | "humidity_setpoint": 14 | ||
} | } | ||
Строка 1504: | Строка 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--> | == Полезные ссылки == <!--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> |