Перейти к содержанию
Переключить боковую панель
Поиск
Поиск
Пользовательские ссылки
русский
Создать учётную запись
Персональные инструменты
expanded
collapsed
Создать учётную запись
Войти
Pages for logged out editors
узнать больше
Навигация
Вернуться на главный сайт:
wirenboard.com
Навигация
Заглавная страница
Читателям
Как внести правку или задать вопрос
Справочник по синтаксису
Инструменты
Служебные страницы
Версия для печати
Пространства имён
Перевести
Варианты
expanded
collapsed
Просмотры
Статистика по языкам
Статистика группы сообщений
Экспорт
Ещё
expanded
collapsed
Экспорт переводов
Параметры
Группа
1-Wire
ADC
ADC:Измерение сопротивлений - технические детали
Backup power for Wiren Board devices
Bluetooth
BMP085
Buzzer
CAN
CMUX
CODESYS
Consumer IR
Controller hardware revisions
CryptodevATECCx08 Auth
DDM845R
Device Tree
DI
Disassembling the controller case
Documentation
Fast Modbus
First power-on Wiren Board 2.8
Gas sensor WB-MSGR
GPIO
Hardware Overview rev. 2.8-rus
Hardware Watchdog Disable
How To Build Linux Kernel
I/O Mapping Matrix
ILI9341
IMX233-OLinuXino-MICRO
Includes. Relay tuning
Inspektor SCADA
IntraHouse
IO modules
LAN9514
MediaWiki:Sitenotice
MOD-MRF24J40
Modbus
Modbus RTU/TCP Slave
Modbus-client
Modem-stretch
MQTT KNX V.0.1.x
NFC
Node-RED
Notification module
NRF24L01
Pin pull-up
Power Meter WB-MAP12H
Power Meter WB-MAP12H Measuring Parameters
Power Meter WB-MAP12H Measuring Registers
Power over Ethernet
Reg
Relay Module Modbus Management
RS-485
RS-485: Wiring and Connection
RS-485:Configuration via Web Interface
Rule Examples
SC16IS752
Sensors Modbus Management
Serial Port
SIM5300E
SIM68V
SIM900R
SNMP
SSH
SVG-Dashboards
TFT01-2.2SP
UART Communication Settings
UEXT-разъемы
Using Wiren Board with peripheral devices
Using Wiren Board with pulsed output devices
View controller files from your computer
Watchdog
WB 4: Errata
WB 5: Errata
WB 6: Errata
WB 7: Errata
WB AC rev. E1.0
WB AC rev. E2.0
WB Dimmers Modbus Registers Map
WB Modbus Devices Firmware Update
WB SH 3.5: Errata
WB SH 3.5: GPIO List
WB SH 3.5: UEXT2
WB-IMX233-CORE
WB-M1W2 1-Wire to Modbus Temperature Measurement Module
WB-MAI2-mini/CC 4-20mA Modbus Analog Inputs
WB-MAO4 0-10V Modbus Analog Outputs
WB-MCM16 Модуль счетных входов 16-канальный
WB-MCM8 Modbus Count Inputs
WB-MGE Modbus-Ethernet Interface Converter
WB-MIO Modbus Interface Converters
WB-MIO-Modbus-Registers
WB-MIR v1 - Modbus IR Remote Control
WB-MIR v2 - Modbus IR Remote Control
Wb-mqtt-db-cli
Wb-mqtt-serial driver
Wb-mqtt-serial templates
WB-MR3xV and WB-MR6xV Modbus Relay Modules
WB-MR6F Modbus Relay Module
WB-MR: Errata
WB-MRM2-mini Modbus Relay Modules
WB-MRPS6 Modbus Relay Modules
WB-MRWL3 Modbus Relay Modules
WB-MSW v.3 Modbus Sensor
WB-MSW v.4 Modbus Sensor
WB-MSWv 3 registers
WB-MSx Consumer IR Manual
WB-UPS Backup power supply for DIN rails
WB5 root password recovery
WB6 root password recovery
WBC-4G v.2
WBE-DI-DR-3
WBE-MICROSD Extension Module
WBE2-AO-10V-2 Analog Outputs 0-10V Extension Module
WBE2-DI-DR-3 Dry Contact Inputs Extension Module
WBE2-DO-OC-2 Open Collector Outputs Extension Module
WBE2-DO-R6C-1 Relay Extension Module
WBE2-DO-SSR-2 Dry Contact Outputs Extension Module
WBE2-I-1-WIRE 1-Wire Extension Module
WBE2-I-CAN-ISO CAN Extension Module
WBE2-I-KNX KNX Extension Module
WBE2-I-KNX: Errata
WBE2-I-RS232 RS-232 Extension Module
WBE2-I-RS485-ISO RS485 Extension Module
WBE2R-R-GPS GPS/GLONASS Extension Module
WBE2R-R-LORA v.1 Extension Module
WBE2R-R-ZIGBEE v.1 ZigBee Extension Module
WBE2S-MICROSD MicroSD Extension Module
Wbincludes:1-Wire Network
Wbincludes:1-Wire Warning
Wbincludes:Configuration MOD
Wbincludes:Configuration WBC
Wbincludes:GSM Modem Default Connection Parameters
Wbincludes:How to enter
Wbincludes:Installation MOD WBE2
Wbincludes:Installation MOD WBE2R
Wbincludes:Installing Software In Console
Wbincludes:Mount MOD
Wbincludes:Mount Relay
Wbincludes:Peripherals
Wbincludes:Relay Purpose2
Wbincludes:Revision
Wbincludes:Setup MOD
Wbincludes:WB-MSW v.3 Body Painting
Wbincludes:WB-MSW v.3 Functions
Wbincludes:WB-MSW v.4 Difference v.3
Wbincludes:WBC-4G Info
Wbincludes:WBC-4G Specifications
Wbincludes:WebUI Change Access Level
WBIO-AI-DCM-4 I/O Module
WBIO-AI-DV-12 Analog Inputs
WBIO-AI-DV-12/4-20mA Analog Inputs
WBIO-AO-10V-8 Analog Outputs 0-10V
WBIO-DI-DR-14 I/O Module
WBIO-DI-DR-16 I/O Module
WBIO-DI-DR-8 I/O Module
WBIO-DI-HVD-16 16-Channel 230V Detector Module
WBIO-DI-HVD-8 8-Channel 230V Detector Module
WBIO-DI-WD-14 Discrete Inputs
WBIO-DO-HS-8 Discrete Outputs
WBIO-DO-R10A-8 Relay Module
WBIO-DO-R10R-4 Relay Module For Roller Shutter
WBIO-DO-R1G-16 1A Relay Module
WBIO-DO-SSR-8 Discrete Outputs Dry Contact
WBMZ-BATTERY - модуль резервного питания
WBMZ2-BATTERY Backup Power Module
Wellpro
Wi-Fi Old
Wiren Board 2.8. Исходные файлы
Wiren Board 4: GPIO List
Wiren Board 4: Модуль расширения RS-232
Wiren Board 4:Аппаратная часть
Wiren Board 4:Первое включение
Wiren Board 5.1: GPIO List
Wiren Board 5.5: GPIO List
Wiren Board 5.8:Список GPIO
Wiren Board 5: Build an image to download in USB Mass-Storage mode
Wiren Board 5: GPIO List
Wiren Board 5: Recovery
Wiren Board 5: Модуль расширения 1-Wire
Wiren Board 5: Модуль расширения ADC
Wiren Board 5: Модуль расширения GPS/Glonass
Wiren Board 5: Модуль расширения RS-232 (WBE-I-RS232)
Wiren Board 5: Модуль расширения RS485-ISO
Wiren Board 5:Аппаратная часть
Wiren Board 5:Первое включение
Wiren Board 5:Схемотехника
Wiren Board 6.1: GPIO List
Wiren Board 6.2: Peripherals
Wiren Board 6.7: Peripherals
Wiren Board 6: Hardware
Wiren Board 6: WBE2R-R-GPS GPS/GLONASS Extension Module:NTPD PPS Setup
Wiren Board 6: Модуль расширения 433MHz (WBE2S-R-433MHZ)
Wiren Board Device Modbus Address
Wiren Board Devices Modbus Management
Wiren Board NETMON-1
Wiren Board NETMON-1: GPIO List
Wiren Board NETMON-2
Wiren Board Smart Home 3.5
Wiren Board Smart Home rev. 3.5
Wiren Board Software
Wiren Board Web Interface
Wiren Board Web Interface 1.0
Wiren Board: Extension Modules
Wirenboard6:InstallingOnTheRemoteSite
Z-Wave
Zabbix
Датчик SHT1x
Движок правил wb-rules 1.7
Доступ к порту RS-485 контроллера Wiren Board с компьютера
Как разрабатывать ПО для Wiren Board
Консоль
Модули расширения
Модуль ввода-вывода с TTL-уровнями (WBIO-DIO-TTL-8)
Модуль выходов "Открытый коллектор" WBE-DO-OС-2
Модуль выходов "сухой контакт" (оптореле) WBE-DO-SSR-2
Модуль наличия напряжения (WBIO-DI-LVD-16)
Модуль наличия напряжения (WBIO-DI-LVD-8)
Модуль расширения CAN (WBE-I-CAN-ISO)
Модуль расширения CAN (WBE-I-CAN-ISO) wb6
Модуль расширения DAC (WBE-AO-10V-2)
Модуль расширения KNX (WBE-I-KNX)
Модуль релейных выходов 3A (WBIO-DO-R3A-8)
Модуль релейных выходов WBE-DO-R6C-1
ПО МКА-3
Первое включение Wiren Board Smart Home
Периферийные устройства
Периферийные устройства с интерфейсом RS-485 серии WB-xxxx
Питание USB-портов
Получение точного времени через GPS/Glonass
Потеря файлов при обновлении ПО
Работа с GPIO
Сounters Pulsar
Сборка образа
Сборка образов прошивки
Создание microSD-карты с образом
Список GPIO
Стандартный образ ФС
Уникальные идентификаторы
Управление низковольтной нагрузкой
Устарело: низкоуровневая работа с ADC в Wiren Board 2, 3 и 4
Участник:EvgenyBoger/CT309-test
Участник:EvgenyBoger/test2
Участник:EvgenyBoger/testtrans
Участник:EvgenyBoger/testtrans2
Участник:EvgenyBoger/Wbincludes:Revision
Шаблон:Node-RED Installing plugin
Шаблон:Testtrans3
Язык
aa - Afar
ab - Abkhazian
abs - Ambonese Malay
ace - Achinese
ady - Adyghe
ady-cyrl - Adyghe (Cyrillic script)
aeb - Tunisian Arabic
aeb-arab - Tunisian Arabic (Arabic script)
aeb-latn - Tunisian Arabic (Latin script)
af - Afrikaans
ak - Akan
aln - Gheg Albanian
alt - Southern Altai
am - Amharic
ami - Amis
an - Aragonese
ang - Old English
anp - Angika
ar - Arabic
arc - Aramaic
arn - Mapuche
arq - Algerian Arabic
ary - Moroccan Arabic
arz - Egyptian Arabic
as - Assamese
ase - American Sign Language
ast - Asturian
atj - Atikamekw
av - Avaric
avk - Kotava
awa - Awadhi
ay - Aymara
az - Azerbaijani
azb - South Azerbaijani
ba - Bashkir
ban - Balinese
ban-bali - ᬩᬲᬩᬮᬶ
bar - Bavarian
bbc - Batak Toba
bbc-latn - Batak Toba (Latin script)
bcc - Southern Balochi
bci - wawle
bcl - Central Bikol
be - Belarusian
be-tarask - Belarusian (Taraškievica orthography)
bg - Bulgarian
bgn - Western Balochi
bh - Bhojpuri
bho - Bhojpuri
bi - Bislama
bjn - Banjar
bm - Bambara
bn - Bangla
bo - Tibetan
bpy - Bishnupriya
bqi - Bakhtiari
br - Breton
brh - Brahui
bs - Bosnian
btm - Batak Mandailing
bto - Iriga Bicolano
bug - Buginese
bxr - Russia Buriat
ca - Catalan
cbk-zam - Chavacano
cdo - Min Dong Chinese
ce - Chechen
ceb - Cebuano
ch - Chamorro
cho - Choctaw
chr - Cherokee
chy - Cheyenne
ckb - Central Kurdish
co - Corsican
cps - Capiznon
cr - Cree
crh - Crimean Turkish
crh-cyrl - Crimean Tatar (Cyrillic script)
crh-latn - Crimean Tatar (Latin script)
cs - Czech
csb - Kashubian
cu - Church Slavic
cv - Chuvash
cy - Welsh
da - Danish
dag - Dagbani
de - German
de-at - Austrian German
de-ch - Swiss High German
de-formal - German (formal address)
din - Dinka
diq - Zazaki
dsb - Lower Sorbian
dtp - Central Dusun
dty - Doteli
dv - Divehi
dz - Dzongkha
ee - Ewe
egl - Emilian
el - Greek
eml - Emiliano-Romagnolo
en - English
en-ca - Canadian English
en-gb - British English
eo - Esperanto
es - Spanish
es-419 - Latin American Spanish
es-formal - español (formal)
et - Estonian
eu - Basque
ext - Extremaduran
fa - Persian
ff - Fulah
fi - Finnish
fit - Tornedalen Finnish
fj - Fijian
fo - Faroese
fr - French
frc - Cajun French
frp - Arpitan
frr - Northern Frisian
fur - Friulian
fy - Western Frisian
ga - Irish
gag - Gagauz
gan - Gan Chinese
gan-hans - Gan (Simplified)
gan-hant - Gan (Traditional)
gcr - Guianan Creole
gd - Scottish Gaelic
gl - Galician
glk - Gilaki
gn - Guarani
gom - Goan Konkani
gom-deva - Goan Konkani (Devanagari script)
gom-latn - Goan Konkani (Latin script)
gor - Gorontalo
got - Gothic
grc - Ancient Greek
gsw - Swiss German
gu - Gujarati
guc - Wayuu
guw - gungbe
gv - Manx
ha - Hausa
hak - Hakka Chinese
haw - Hawaiian
he - Hebrew
hi - Hindi
hif - Fiji Hindi
hif-latn - Fiji Hindi (Latin script)
hil - Hiligaynon
ho - Hiri Motu
hr - Croatian
hrx - Hunsrik
hsb - Upper Sorbian
ht - Haitian Creole
hu - Hungarian
hu-formal - magyar (formal)
hy - Armenian
hyw - Western Armenian
hz - Herero
ia - Interlingua
id - Indonesian
ie - Interlingue
ig - Igbo
ii - Sichuan Yi
ik - Inupiaq
ike-cans - Eastern Canadian (Aboriginal syllabics)
ike-latn - Eastern Canadian (Latin script)
ilo - Iloko
inh - Ingush
io - Ido
is - Icelandic
it - Italian
iu - Inuktitut
ja - Japanese
jam - Jamaican Creole English
jbo - Lojban
jut - Jutish
jv - Javanese
ka - Georgian
kaa - Kara-Kalpak
kab - Kabyle
kbd - Kabardian
kbd-cyrl - Kabardian (Cyrillic script)
kbp - Kabiye
kcg - Tyap
kg - Kongo
khw - Khowar
ki - Kikuyu
kiu - Kirmanjki
kj - Kuanyama
kjp - Eastern Pwo
kk - Kazakh
kk-arab - Kazakh (Arabic script)
kk-cn - Kazakh (China)
kk-cyrl - Kazakh (Cyrillic script)
kk-kz - Kazakh (Kazakhstan)
kk-latn - Kazakh (Latin script)
kk-tr - Kazakh (Turkey)
kl - Kalaallisut
km - Khmer
kn - Kannada
ko - Korean
ko-kp - Korean (North Korea)
koi - Komi-Permyak
kr - Kanuri
krc - Karachay-Balkar
kri - Krio
krj - Kinaray-a
krl - Karelian
ks - Kashmiri
ks-arab - Kashmiri (Arabic script)
ks-deva - Kashmiri (Devanagari script)
ksh - Colognian
ku - Kurdish
ku-arab - Kurdish (Arabic script)
ku-latn - Kurdish (Latin script)
kum - Kumyk
kv - Komi
kw - Cornish
ky - Kyrgyz
la - Latin
lad - Ladino
lb - Luxembourgish
lbe - Lak
lez - Lezghian
lfn - Lingua Franca Nova
lg - Ganda
li - Limburgish
lij - Ligurian
liv - Livonian
lki - Laki
lld - Ladin
lmo - Lombard
ln - Lingala
lo - Lao
loz - Lozi
lrc - Northern Luri
lt - Lithuanian
ltg - Latgalian
lus - Mizo
luz - Southern Luri
lv - Latvian
lzh - Literary Chinese
lzz - Laz
mad - Madurese
mai - Maithili
map-bms - Basa Banyumasan
mdf - Moksha
mg - Malagasy
mh - Marshallese
mhr - Eastern Mari
mi - Maori
min - Minangkabau
mk - Macedonian
ml - Malayalam
mn - Mongolian
mni - Manipuri
mnw - Mon
mo - Moldovan
mr - Marathi
mrh - Mara
mrj - Western Mari
ms - Malay
ms-arab - بهاس ملايو
mt - Maltese
mus - Muscogee
mwl - Mirandese
my - Burmese
myv - Erzya
mzn - Mazanderani
na - Nauru
nah - Nāhuatl
nan - Min Nan Chinese
nap - Neapolitan
nb - Norwegian Bokmål
nds - Low German
nds-nl - Low Saxon
ne - Nepali
new - Newari
ng - Ndonga
nia - Nias
niu - Niuean
nl - Dutch
nl-informal - Nederlands (informeel)
nn - Norwegian Nynorsk
no - Norwegian
nov - Novial
nqo - N’Ko
nrm - Norman
nso - Northern Sotho
nv - Navajo
ny - Nyanja
nys - Nyungar
oc - Occitan
ojb - Ojibwemowin
olo - Livvi-Karelian
om - Oromo
or - Odia
os - Ossetic
pa - Punjabi
pag - Pangasinan
pam - Pampanga
pap - Papiamento
pcd - Picard
pdc - Pennsylvania German
pdt - Plautdietsch
pfl - Palatine German
pi - Pali
pih - Norfuk / Pitkern
pl - Polish
pms - Piedmontese
pnb - Western Punjabi
pnt - Pontic
prg - Prussian
ps - Pashto
pt - Portuguese
pt-br - Brazilian Portuguese
pwn - Paiwan
qu - Quechua
qug - Chimborazo Highland Quichua
rgn - Romagnol
rif - Riffian
rm - Romansh
rmc - Carpathian Romani
rmy - Vlax Romani
rn - Rundi
ro - Romanian
roa-tara - Tarantino
ru - Russian
rue - Rusyn
rup - Aromanian
ruq - Megleno-Romanian
ruq-cyrl - Megleno-Romanian (Cyrillic script)
ruq-latn - Megleno-Romanian (Latin script)
rw - Kinyarwanda
sa - Sanskrit
sah - Sakha
sat - Santali
sc - Sardinian
scn - Sicilian
sco - Scots
sd - Sindhi
sdc - Sassarese Sardinian
sdh - Southern Kurdish
se - Northern Sami
sei - Seri
ses - Koyraboro Senni
sg - Sango
sgs - Samogitian
sh - Serbo-Croatian
shi - Tachelhit
shi-latn - Tachelhit (Latin script)
shi-tfng - Tachelhit (Tifinagh script)
shn - Shan
shy - Shawiya
shy-latn - Shawiya (Latin script)
si - Sinhala
simple - Simple English
sjd - Kildin Sami
sje - Pite Sami
sk - Slovak
skr - Saraiki
skr-arab - Saraiki (Arabic script)
sl - Slovenian
sli - Lower Silesian
sm - Samoan
sma - Southern Sami
smn - Inari Sami
sn - Shona
so - Somali
sq - Albanian
sr - Serbian
sr-ec - Serbian (Cyrillic script)
sr-el - Serbian (Latin script)
srn - Sranan Tongo
ss - Swati
st - Southern Sotho
stq - Saterland Frisian
sty - себертатар
su - Sundanese
sv - Swedish
sw - Swahili
szl - Silesian
szy - Sakizaya
ta - Tamil
tay - Tayal
tcy - Tulu
te - Telugu
tet - Tetum
tg - Tajik
tg-cyrl - Tajik (Cyrillic script)
tg-latn - Tajik (Latin script)
th - Thai
ti - Tigrinya
tk - Turkmen
tl - Tagalog
tly - Talysh
tly-cyrl - толыши
tn - Tswana
to - Tongan
tpi - Tok Pisin
tr - Turkish
tru - Turoyo
trv - Taroko
ts - Tsonga
tt - Tatar
tt-cyrl - Tatar (Cyrillic script)
tt-latn - Tatar (Latin script)
tum - Tumbuka
tw - Twi
ty - Tahitian
tyv - Tuvinian
tzm - Central Atlas Tamazight
udm - Udmurt
ug - Uyghur
ug-arab - Uyghur (Arabic script)
ug-latn - Uyghur (Latin script)
uk - Ukrainian
ur - Urdu
uz - Uzbek
uz-cyrl - Uzbek (Cyrillic script)
uz-latn - Uzbek (Latin script)
ve - Venda
vec - Venetian
vep - Veps
vi - Vietnamese
vls - West Flemish
vmf - Main-Franconian
vo - Volapük
vot - Votic
vro - Võro
wa - Walloon
war - Waray
wls - Wallisian
wo - Wolof
wuu - Wu Chinese
xal - Kalmyk
xh - Xhosa
xmf - Mingrelian
xsy - Saisiyat
yi - Yiddish
yo - Yoruba
yue - Cantonese
za - Zhuang
zea - Zeelandic
zgh - Standard Moroccan Tamazight
zh - Chinese
zh-cn - Chinese (China)
zh-hans - Simplified Chinese
zh-hant - Traditional Chinese
zh-hk - Chinese (Hong Kong)
zh-mo - Chinese (Macau)
zh-my - Chinese (Malaysia)
zh-sg - Chinese (Singapore)
zh-tw - Chinese (Taiwan)
zu - Zulu
Формат
Экспорт для оффлайнового перевода
Экспорт в родном формате
Вывести
<languages/> {{DISPLAYTITLE: Rule Examples}} === Control tracking === This simplest rule keeps track of a control and sets another control to the same state. For example, a rule can turn on a siren and a lamp if a motion sensor detects movement. In the example, the motion sensor is connected to the «dry contact» input, control type «switch». The siren is connected to the built-in Wiren Board relay, and the lamp is connected to the relay box via Modbus. When the «dry contact» input (motion sensor output) is closed, «1» is supplied to the lamp and the relay, and «0» when it is off. The rule is triggered every time the control value «D1_IN» of the device «wb-gpio» is changed. The new value of this control is passed to the rule code as a variable newValue. <syntaxhighlight lang="ecmascript"> defineRule({ whenChanged: "wb-gpio/D1_IN", then: function (newValue, devName, cellName) { dev["wb-gpio/Relay_2"] = newValue; dev["wb-mrm2_6/Relay 1"] = newValue; } }); </syntaxhighlight> The same, but with a virtual device as an event source. Usage example: scripted button that turns on/off the siren and light bulb. <syntaxhighlight lang="ecmascript"> defineVirtualDevice("simple_test", { title: "Simple switch", cells: { enabled: { type: "switch", value: false }, } }); defineRule("simple_switch", { whenChanged: "simple_test/enabled", then: function (newValue, devName, cellName) { dev["wb-gpio/Relay_2"] = newValue; dev["wb-mrm2_6/Relay 1"] = newValue; } }); </syntaxhighlight> === Motion detection with timeout === A motion detector with a «dry contact» output is connected to input D2. When motion is detected, it shorts D2 and GND and status «1» appears on the corresponding <code>wb-gpio/D2_IN</code> channel. The lighting is connected via a built-in relay corresponding to the <code>wb-gpio/Relay_1</code> channel. The rule works like this: * when movement appears, the light turns on. If a thirty-second «off» timer was previously started, this timer is disabled; * when motion is lost, a thirty second «off» timer is started. If he manages to reach the end, the light turns off. <syntaxhighlight lang="ecmascript"> var motion_timer_1_timeout_ms = 30 * 1000; var motion_timer_1_id = null; defineRule("motion_detector_1", { whenChanged: "wb-gpio/D2_IN", then: function (newValue, devName, cellName) { if (newValue) { dev["wb-gpio/Relay_1"] = true; if (motion_timer_1_id) { clearTimeout(motion_timer_1_id); } motion_timer_1_id = setTimeout(function () { dev["wb-gpio/Relay_1"] = false; motion_timer_1_id = null; }, motion_timer_1_timeout_ms); } }, }); </syntaxhighlight> === Creating similar rules === If you need several such motion detectors, then in order not to copy the code, you can wrap the creation of the rule and variables in a function: <syntaxhighlight lang="ecmascript"> function makeMotionDetector(name, timeout_ms, detector_control, relay_control) { var motion_timer_id = null; defineRule(name, { whenChanged: "wb-gpio/" + detector_control, then: function(newValue, devName, cellName) { if (!newValue) { dev["wb-gpio/relay_control"] = true; if (motion_timer_id) { clearTimeout(motion_timer_id); } motion_timer_id = setTimeout(function() { dev["wb-gpio/relay_control"] = false; motion_timer_id = null; }, timeout_ms); } } }); } makeMotionDetector("motion_detector_1", 20000, "EXT1_DR1", "EXT2_R3A1"); makeMotionDetector("motion_detector_2", 10000, "EXT1_DR2", "EXT2_R3A2"); makeMotionDetector("motion_detector_3", 10000, "EXT1_DR3", "EXT2_R3A3"); </syntaxhighlight> === Activate the rule only at a certain time === The rule is the same as in the previous section, but only runs from 9:30 am to 5:10 pm UTC. <syntaxhighlight lang="ecmascript"> var motion_timer_1_timeout_ms = 5 * 1000; var motion_timer_1_id = null; defineRule("motion_detector_1", { whenChanged: "wb-gpio/A1_IN", then: function (newValue, devName, cellName) { var date = new Date(); // time point marking the beginning of the interval // i.e. "today, at HH:MM". All dates are in UTC! var date_start = new Date(date); date_start.setHours(9); date_start.setMinutes(30); // time point marking the end of the interval var date_end = new Date(date); date_end.setHours(17); date_end.setMinutes(10); // if time is between 09:30 and 17:10 UTC if ((date > date_start) && (date < date_end)) { if (newValue) { dev["wb-gpio/EXT1_R3A1"] = 1; if (motion_timer_1_id) { clearTimeout(motion_timer_1_id); } motion_timer_1_id = setTimeout(function () { dev["wb-gpio/EXT1_R3A1"] = 0; motion_timer_1_id = null; }, motion_timer_1_timeout_ms); } } } }); </syntaxhighlight> === Rolling shutters === One relay turns on the motor that raises the curtains, the second relay turns on the motor that lowers the curtains. The rule ensures that both relays are not turned on at the same time. In addition, the rule turns off the engines after a specified time after being turned on. <syntaxhighlight lang="ecmascript"> (function() { //don't touch this line var suffix = "1"; // must be different in different JS files var relay_up_device = "lc103_4"; var relay_up_control = "Relay 1"; var relay_down_device = "lc103_4"; var relay_down_control = "Relay 2"; var timeout_s = 15; // End of settings var relay_up_timer_id = null; var relay_down_timer_id = null; defineRule( "roller_shutter_up_on" + suffix, { asSoonAs: function() { return dev["relay_up_device/relay_up_control"]; }, then: function () { if (relay_up_timer_id) { relay_up_timer_id = clearTimeout(relay_up_timer_id); }; relay_up_timer_id = setTimeout(function() { return dev["relay_up_device/relay_up_control"] = 0; }, timeout_s * 1000); } }); defineRule("roller_shutter_down_on" + suffix, { asSoonAs: function() { return dev[relay_down_device][relay_down_control]; }, then: function () { if (relay_down_timer_id) { relay_down_timer_id = clearTimeout(relay_down_timer_id); }; relay_down_timer_id = setTimeout(function() { dev["relay_down_device/relay_down_control"] = 0; }, timeout_s * 1000); } }); defineRule("roller_shutter_both_on" + suffix, { asSoonAs: function() { return dev[relay_up_device][relay_up_control] && dev["relay_down_device/relay_down_control"]; }, then: function () { if (relay_up_timer_id) { relay_up_timer_id = clearTimeout(relay_up_timer_id); }; if (relay_down_timer_id) { relay_down_timer_id = clearTimeout(relay_down_timer_id); }; dev[relay_up_device][relay_up_control] = 0; dev["relay_down_device/relay_down_control"] = 0; log("Both roller shutter relays on, switching them off"); } }); })(); </syntaxhighlight> An older version of the same script demonstrates the use of aliases: <syntaxhighlight lang="ecmascript"> (function() { defineAlias("relay_up_1", "lc103_4/Relay 1"); defineAlias("relay_down_1", "lc103_4/Relay 2"); var timeout_s = 15; defineRule("roller_shutter_1_up_on", { asSoonAs: function() { return relay_up_1; }, then: function () { setTimeout(function() { relay_up_1 = 0; }, timeout_s * 1000); } }); defineRule("roller_shutter_1_down_on", { asSoonAs: function() { return relay_down_1; }, then: function () { setTimeout(function() { relay_down_1 = 0; }, timeout_s * 1000); } }); defineRule("roller_shutter_1_both_on", { asSoonAs: function() { return relay_up_1 && relay_down_1; }, then: function () { relay_up_1 = 0; relay_down_1 = 0; log("Both roller shutter relays on, switching them off"); } }); })(); </syntaxhighlight> == Impulse counters == Pulse counter connected to WB-MCM8. Gives out 1 pulse per 10 liters of water. When connected, the meter showed readings of 123.120 m³, which is equal to 123120 liters of water. The WB-MCM8 had 7 pulses when plugged in. <syntaxhighlight lang="ecmascript"> var meterCorrection = 123120 // Кcorrection value of the meter in liters var counterCorrection = 7 // WB-MCM8 correction value in pulses var inpulseValue = 10 // Number of liters per impulse defineVirtualDevice("water_meters", { // We create a virtual device for display in the web interface. title: "Water meters", cells: { water_meter_1: { type: "value", value: 0 }, } }); defineRule("water_meter_1", { whenChanged: "wb-mcm8_29/Input 1 counter", then: function(newValue, devName, cellName) { if(newValue){ dev["water_meters/water_meter_1"] = ((parseInt(newValue) - counterCorrection) * inpulseValue) + meterCorrection; // We multiply the value of the counter by the number of liters / pulse and add the correction value. } } }); </syntaxhighlight> == Handling click counters == {{Anchors|press-actions}} === Description === The latest firmware versions of Wiren Board devices can recognize the types of button presses connected to the inputs and broadcast them via [[Modbus]] to the Wiren Board controller. For information on how the device recognizes types of clicks, read its documentation. To process clicks, you need to track the state of the counter of the desired type of click on the controller and, when it changes, perform an action. Handling counters is conveniently done on [[wb-rules]], but you can use any automation tool like [[Node-RED]]. To speed up meter polling, configure [[RS-485:Configuration via Web Interface#poll-period |poll period]]. === Examples === {{YouTube |link=https://youtu.be/C60KB7TCeKg |text= Rule example }} In the example, we are using the [[WB-MCM8 Modbus Count Inputs | WB-MCM8]] to control the first dimmer channel [[WB-MDM3 230V Modbus Dimmer | WB-MDM3]]: # Short press turns on the channel. # Double - turns off the channel. # Long - increases brightness. # Short, then long - reduces brightness. Since changing the brightness requires a time-consuming action, we use a timer. We also control the state of the input with the button and stop the action when the button is released. <syntaxhighlight lang="js"> /* ---------------------------- */ /* 1. Single Press Counter: On action*/ /* ---------------------------- */ defineRule({ whenChanged: "wb-mcm8_20/Input 1 Single Press Counter", then: function (newValue, devName, cellName) { dev["wb-mdm3_58/K1"] = true; } }); /* ---------------------------- */ /* 2. Double Press Counter: Off action*/ /* ---------------------------- */ defineRule({ whenChanged: "wb-mcm8_20/Input 1 Double Press Counter", then: function (newValue, devName, cellName) { dev["wb-mdm3_58/K1"] = false; } }); /* --------------------------------------- */ /* 3. Long Press Counter: Increase brightness */ /* --------------------------------------- */ defineRule({ whenChanged: "wb-mcm8_20/Input 1 Long Press Counter", then: function (newValue, devName, cellName) { // Start a timer that will increase the value of the control startTicker("input1_long_press", 75); } }); // A rule that will increase the brightness on a timer defineRule({ when: function () { return timers["input1_long_press"].firing; }, then: function () { var i = dev["wb-mdm3_58/Channel 1"]; if (i < 100 && dev["wb-mcm8_20/Input 1"]) { i++ dev["wb-mdm3_58/Channel 1"] = i } else { timers["input1_long_press"].stop(); } } }); /* -------------------------------------------- */ /* 4. Shortlong Press Counter: Decrease brightness */ /* -------------------------------------------- */ defineRule({ whenChanged: "wb-mcm8_20/Input 1 Shortlong Press Counter", then: function (newValue, devName, cellName) { // Start a timer that will decrease the value of the control startTicker("input1_shortlong_press", 75); } }); // A rule that will decrease the brightness on a timer defineRule({ when: function () { return timers["input1_shortlong_press"].firing; }, then: function () { var i = dev["wb-mdm3_58/Channel 1"]; if (i > 0 && dev["wb-mcm8_20/Input 1"]) { i-- dev["wb-mdm3_58/Channel 1"] = i } else { timers["input1_shortlong_press"].stop(); } } }); </syntaxhighlight> === Generic module for wb-rules === We wrote a module for wb-rules [https://github.com/wirenboard/wb-community/tree/main/wb-press-actions wb-press-actions] that makes it easy to handle clicks in your scripts. == Sensor MSW v.3 == When connecting the WB-MSW v.3 sensor to the Wiren Board controller, it is possible to create interesting scenarios using data from the sensor. For example, turn on the light when moving, signal with LEDs when the CO2 or VOC value is exceeded, turn on the air conditioner if it is hot or the air humidifier if the air is too dry. Rules are created individually for tasks. Here we will give some examples to understand the principle of working with the sensor. More examples of writing rules can be found in the '''[[Движок правил wb-rules | Rules engine wb-rules]]''' documentation. === CO2 === When the CO2 concentration is less than 650, we flash green once every 10 seconds. When the CO2 concentration is over 651, but less than 1000, we flash yellow once every 5 seconds. When the CO2 concentration is over 1001, we flash red once a second. <div class="mw-collapsible mw-collapsed"; style="width:600px; overflow: hidden;"> <syntaxhighlight lang="ecmascript"> defineRule("msw3_co2", { whenChanged: "wb-msw-v3_97/CO2", then: function(newValue, devName, cellName) { var co2_good = newValue < 650; var co2_middle = newValue < 1000 && newValue > 651; var co2_bad = newValue > 1001; if (co2_good) { dev["devName/Green LED"] = true; dev["devName/Red LED"] = false; dev["devName/LED Period (s)"] = 10; } if (co2_middle) { dev["devName/Green LED"] = true; dev["devName/Red LED"] = true; dev["devName/LED Period (s)"] = 5; } if (co2_bad) { dev["devName/Green LED"] = false; dev["devName/Red LED"] = true; dev["devName/LED Period (s)"] = 1; } } }); </syntaxhighlight> </div> === Max Motion === "Max Motion" - the maximum value of the motion sensor for N time. Time from 1 to 60 seconds can be set in register 282. The default is 10 seconds. When the Max Motion value reaches 50, we check whether the room is sufficiently lit, if not, turn on the light. As soon as the Max Motion value drops below 50, turn off the light. <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 (dev["wb-msw-v3_97/Illuminance"] < 50) { dev["wb-mr3_11/K1"] = true; } } else { dev["wb-mr3_11/K1"] = false; } } }); </syntaxhighlight> </div> </div> === System rules === Many of the indications that are visible in the web interface of the controller out of the box are also created by rules on the wb-rules engine. Their code is here: [https://github.com/wirenboard/wb-rules-system https://github.com/wirenboard/wb-rules-system]. The system rules are collected in the <code>wb-rules-system</code> package, the script files on the controller are located in the <code>/usr/share/wb-rules-system/</code> folder. A few examples of system rules are below. === Rule for tweeters === [https://github.com/contactless/wb-rules-system/blob/master/rules/buzzer.js Rule] creates a virtual buzzer device with volume and frequency sliders and a mute button. <syntaxhighlight lang="ecmascript"> defineVirtualDevice("buzzer", { title: "Buzzer", // cells: { frequency : { type : "range", value : 3000, max : 7000, }, volume : { type : "range", value : 10, max : 100, }, enabled : { type : "switch", value : false, }, } }); // setup pwm2 runShellCommand("echo 2 > /sys/class/pwm/pwmchip0/export"); function _buzzer_set_params() { var period = parseInt(1.0 / dev.buzzer.frequency * 1E9); var duty_cycle = parseInt(dev.buzzer.volume * 1.0 / 100 * period * 0.5); runShellCommand("echo " + period + " > /sys/class/pwm/pwmchip0/pwm2/period"); runShellCommand("echo " + duty_cycle + " > /sys/class/pwm/pwmchip0/pwm2/duty_cycle"); }; defineRule("_system_buzzer_params", { whenChanged: [ "buzzer/frequency", "buzzer/volume", ], then: function (newValue, devName, cellName) { if ( dev.buzzer.enabled) { _buzzer_set_params(); } } }); defineRule("_system_buzzer_onof", { whenChanged: "buzzer/enabled", then: function (newValue, devName, cellName) { if ( dev.buzzer.enabled) { _buzzer_set_params(); runShellCommand("echo 1 > /sys/class/pwm/pwmchip0/pwm2/enable"); } else { runShellCommand("echo 0 > /sys/class/pwm/pwmchip0/pwm2/enable"); } } }); </syntaxhighlight> === Power status rule === [https://github.com/contactless/wb-rules-system/blob/master/rules/power_status.js Rule] creates a virtual device that reports the current power status. Two ADC channels are used as input data: battery voltage measurement and input voltage measurement. The following logic is implemented: 1. If the input voltage is less than the battery voltage, then the board is powered by the battery. In this case, 0V is also displayed as the input voltage. 2. If the input voltage is greater than the battery voltage, then the board is powered by an external power source. The measurement from the Vin channel is displayed as the input voltage. To illustrate, the rules use two different ways of triggering: by changing the value of the control (rule _system_track_vin) and by changing the value of the expression (the other two). <syntaxhighlight lang="ecmascript"> defineVirtualDevice("power_status", { title: "Power status", // cells: { 'working on battery' : { type : "switch", value : false, readonly : true }, 'Vin' : { type : "voltage", value : 0 } } }); defineRule("_system_track_vin", { whenChanged: "wb-adc/Vin", then: function() { if (dev["wb-adc/Vin"] < dev["wb-adc/BAT"] ) { dev["power_status/Vin"] = 0; } else { dev["power_status/Vin"] = dev["wb-adc/Vin"] ; } } }); defineRule("_system_dc_on", { asSoonAs: function () { return dev["wb-adc/Vin"] > dev["wb-adc/BAT"]; }, then: function () { dev["power_status/working on battery"] = false; } }); defineRule("_system_dc_off", { asSoonAs: function () { return dev["wb-adc/Vin"] <= dev["wb-adc/BAT"]; }, then: function () { dev["power_status/working on battery"] = true; } }); </syntaxhighlight> == Thermostat == An example of a simple thermostat from the [https://support.wirenboard.com/t/novaya-versiya-dvizhka-pravil/4196/158 topic on the support portal]. <syntaxhighlight lang="js"> defineVirtualDevice("Termostat", { title: "Termostat", cells: { // =============== hallway underfloor heating "R01-TS16-1-mode": {//mode 0-manual 1-scheduled type: "switch", value: false, }, "R01-TS16-1-setpoint": {//setting type: "range", value: 25, max: 30, readonly: false }, "R01-TS16-1-lock": {//blockage in visualization 0-unlocked 1-blocked type: "switch", value: false, },....... var hysteresis = 0.5; function Termostat(name, temp, setpoint, TS, TS_onoff) { defineRule(name, { whenChanged: temp, //when the sensor state changes then: function (newValue, devName, cellName) { // do the following if (dev[TS_onoff]) { if ( newValue < dev[setpoint] - hysteresis) { //if the sensor temperature is less than the setpoint - hysteresis dev[TS] = true; } if ( newValue > dev[setpoint] + hysteresis) { //if the sensor temperature is greater than the virtual setpoint + hysteresis dev[TS] = false; } } else dev[TS] = false; } }); } Termostat("R01-TS16-1", "A60-M1W3/External Sensor 1", "Termostat/R01-TS16-1-setpoint", "wb-gpio/EXT4_R3A1", "Termostat/R01-TS16-1-onoff"); // Hallway underfloor heating </syntaxhighlight> == Sending commands via RS-485 == For example, send a command to the device on the port /dev/ttys0 (corresponds to the hardware port RS-485-ISO on the [[Special:MyLanguage/Wiren Board 4|Wiren Board 4]]). To do this, we will use the rules engine and the ability to execute arbitrary shell commands. See [https://github.com/contactless/wb-rules#%D0%94%D1%80%D1%83%D0%B3%D0%B8%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D1%91%D0%BD%D0%BD%D1%8B%D0%B5-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8-%D0%B8-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5 documentation] for details. Create a virtual device with switch type control via rules engine. When you turn on the switch a command will be sent: (Set Brightness ch. 00=0xff) for Uniel UCH-M141: <pre> FF FF 0A 01 FF 00 00 0A </pre> When you turn off the switch a command will be sent: (set channel brightness 00=0x00) for Uniel UCH-M141: <pre> FF FF 0A 01 00 00 00 0B </pre> 1. Port setting To configure the / dev/ttyNSC0 port to 9600 speed, run the following command <pre> stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8 </pre> 2. Sending a command Sending data is done with the following shell command: <pre> /usr/bin/printf '\xFF\xFF\x0A\x01\xD1\x06\x00\xE2' >/dev/ttyNSC0 </pre> where "\xFF\xFF\x0A\x01\xD1\x06\x00\xE2" - is the entry of a "FF FF 0A 01 D1 06 00 E2" command. 3. Create the new rules file <code>/etc/wb-rules/rs485_cmd.js</code> in the rules engine The file can be edited with vim, nano, or mcedit in an ssh session on the device, or it can be downloaded with SCP. <pre> root@wirenboard:~# mcedit /etc/wb-rules/rs485_cmd.js </pre> 4. Describe the virtual device in the file <syntaxhighlight lang="ecmascript"> defineVirtualDevice("rs485_cmd", { title: "Send custom command to RS-485 port", cells: { enabled: { type: "switch", value: false }, } }); </syntaxhighlight> 5. Restart wb-rules and check the operation <pre> root@wirenboard:~# /etc/init.d/wb-rules restart root@wirenboard:~# tail -f /var/log/messages </pre> There should be no error messages in the log (exit via control-c) A new device "Send custom command to RS-485 port" should appear in the Devices section of the web interface. 6. Add a function to configure the port. <syntaxhighlight lang="ecmascript"> function setup_port() { runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8"); } </syntaxhighlight> 7. Let's describe the rules for turning the switch on and off <syntaxhighlight lang="ecmascript"> defineRule("_rs485_switch_on", { asSoonAs: function () { return dev.rs485_cmd.enabled; }, then: function() { runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\xff\\x00\\x00\\x0a' > /dev/ttyNSC0"); } }); defineRule("_rs485_switch_off", { asSoonAs: function () { return !dev.rs485_cmd.enabled; }, then: function() { runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\x00\\x00\\x00\\x0b' >/dev/ttyNSC0"); } }); </syntaxhighlight> Note the double shielding. 7. Putting it all together The full contents of the file with the rules: <syntaxhighlight lang="ecmascript"> defineVirtualDevice("rs485_cmd", { title: "Send custom command to RS-485 port", cells: { enabled: { type: "switch", value: false }, } }); function setup_port() { runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8"); } defineRule("_rs485_switch_on", { asSoonAs: function () { return dev.rs485_cmd.enabled; }, then: function() { runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\xff\\x00\\x00\\x0a' > /dev/ttyNSC0"); } }); defineRule("_rs485_switch_off", { asSoonAs: function () { return !dev.rs485_cmd.enabled; }, then: function() { runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\x00\\x00\\x00\\x0b' >/dev/ttyNSC0"); } }); setTimeout(setup_port, 1000); // set setup_port() running 1 second after starting. </syntaxhighlight> == Sending a message via Telegram bot == {{Anchor|telegram}} Messages are sent using [https://core.telegram.org/api#telegram-api Telegram API] via <code>curl</code>. <syntaxhighlight lang="ecmascript"> varmessage = "Text"; // write your message text var token = "TOKEN"; // replace with bot token var chat_id = CHATID; // replace with your chat_id var command = 'curl -s -X POST https://api.telegram.org/bot{}/sendMessage -d chat_id={} -d text="{}"'.format(token, chat_id, message); runShellCommand(command); </syntaxhighlight> == Handling errors when working with serial devices == Implemented by subscribing to all '''meta/error''' topics. <syntaxhighlight lang="ecmascript"> defineVirtualDevice("meta_error_test", { title: "Metaerordisplay", cells: { topic: { type: "text", value: "", readonly: true }, value: { type: "text", value: "", readonly: true }, } }); trackMqtt("/devices/+/controls/+/meta/error", function(message){ log.info("name: {}, value: {}".format(message.topic, message.value)) if (message.value=="r"){ dev["meta_error_test/topic"] = message.topic; dev["meta_error_test/value"] = message.value; } }); </syntaxhighlight> == Custom fields in web interface == [[File:Sample-custom-config-1.png|300px|thumb|right|Example configuration]] [[File:Sample-custom-config-2.png|300px|thumb|right|Example script]] If you need to manually enter temperature and humidity settings in the interface of the Wiren Board controller. An easy way is to do in the defineVirtualDevice() field, make it readonly: false. And it will appear in the web interface in Devices as editable, and the value will be saved in the rules engine. But a complex setup with menus and options cannot be done this way. The correct but tricky way is to create a new tab in the Configs section with editable settings options fields. Three files are required: 1. The output scheme of the html page in the Configs section: /usr/share/wb-mqtt-confed/schemas/test-config.schema.json <syntaxhighlight lang="bash"> { "type":"object", "title":"Test configuration", "description":"Long description configuration", "configFile": { "path":"/etc/test-config.conf", "service":"wb-rules" }, "properties": { "temperature_setpoint": { "type":"number", "title":"Temperature Setpoint (Degrees C)", "default": 25, "propertyOrder": 1, "minimum": 5, "maximum": 40 }, "humidity_setpoint": { "type":"number", "title":"Humidity Setpoint (RH, %)", "default": 60, "propertyOrder": 2, "minimum": 10, "maximum": 95 } }, "required": ["temperature_setpoint", "humidity_setpoint"] } </syntaxhighlight> 2. Description of the default configuration (when saving the form in the web interface, the values will be written to this file) : /etc/test-config.conf <syntaxhighlight lang="bash"> { "temperature_setpoint": 60, "humidity_setpoint": 14 } </syntaxhighlight> 3. Script that updates config : /mnt/data/etc/wb-rules/test-config-script.js <syntaxhighlight lang="bash"> var config = readConfig("/etc/test-config.conf"); log("temperature setpoint is: {}".format(config.temperature_setpoint)); </syntaxhighlight> The last file can also be edited from the web interface on the Scripts tab. In the json file describes the schema of the output html page browser, according to generally accepted mapping standard. Description of keys here: json-schema.org ahhh! After creating the files, you need to restart the services <syntaxhighlight lang="bash"> service wb-mqtt-confed restart service wb-rules restart </syntaxhighlight> When you click Save in the web interface, the wb-rules service will be restarted, and the values of the settings will be written to the rules. == Complex rules with schedules == The object is a grocery store. Various store systems are controlled by feedback from temperature sensors and taking into account the store's work schedule. Not cron-rules are used for schedules, but the libschedule. The libschedule enables and disables rules, which, unlike cron rules, are executed continuously when enabled. For example, we want the lighting to be on from 10 to 17h. The libschedule will follow the «turn on the lights» rule once a minute from 10 am to 17 PM. This means that even if the controller is running intermittently and missed the transition time between schedules (10 am), the controller will still turn on the lights at the first opportunity. lib_schedules.js: <syntaxhighlight lang="ecmascript"> global.__proto__.Schedules = {}; (function(Schedules) { // closing function todayAt(now, hours, minutes) { var date = new Date(now); // i.e. "today, at HH:MM". All dates are in UTC! date.setHours(hours); date.setMinutes(minutes); return date; } function checkScheduleInterval(now, start_time, end_time) { var start_date = todayAt(now, start_time[0], start_time[1]); var end_date = todayAt(now, end_time[0], end_time[1]); log("checkScheduleInterval {} {} {}".format(now, start_date, end_date)); if (end_date >= start_date) { if ((now >= start_date) && (now < end_date)) { return true; } } else { // end date is less than start date, // assuming they belong to a different days (e.g. today and tomorrow) // option 1: what if it's now the day of "end" date? // in this case the following is enough: if (now < end_date) { return true; } // well, that seems not to be the case. ok, // option 2: it's the day of "start" date: if (now >= start_date) { return true; } } return false; } function checkSchedule(schedule, now) { if (now == undefined) { now = new Date(); } for (var i = 0; i < schedule.intervals.length; ++i) { var item = schedule.intervals[i]; if (checkScheduleInterval(now, item[0], item[1])) { log("found matching schedule interval at {}".format(item)); return true; } } return false; } function updateSingleScheduleDevStatus(schedule) { log("updateSingleScheduleDevStatus {}".format(schedule.name)); dev["_schedules"][schedule.name] = checkSchedule(schedule); }; function addScheduleDevCronTasks(schedule) { for (var i = 0; i < schedule.intervals.length; ++i) { var interval = schedule.intervals[i]; for (var j = 0; j < 2; ++j) { // either start or end of the interval var hours = interval[j][0]; var minutes = interval[j][1]; log("cron at " + "0 " + minutes + " " + hours + " * * *"); defineRule("_schedule_dev_{}_{}_{}".format(schedule.name, i, j), { when: cron("0 " + minutes + " " + hours + " * * *"), then: function () { log("_schedule_dev_ {}_{}_{}".format(schedule.name, i, j)); updateSingleScheduleDevStatus(schedule); } }); } } } function addScheduleAutoUpdCronTask(schedule) { defineRule("_schedule_auto_upd_{}".format(schedule.name), { when: cron("@every " + schedule.autoUpdate), then: function() { dev._schedules[schedule.name] = dev._schedules[schedule.name]; } }); } var _schedules = {}; Schedules.registerSchedule = function(schedule) { _schedules[schedule.name] = schedule; }; Schedules.initSchedules = function() { var params = { title: "Schedule Status", cells: {} }; for (var schedule_name in _schedules) { if (_schedules.hasOwnProperty(schedule_name)) { var schedule = _schedules[schedule_name]; params.cells[schedule_name] = {type: "switch", value: false, readonly: true}; } }; defineVirtualDevice("_schedules", params); for (var schedule_name in _schedules) { if (_schedules.hasOwnProperty(schedule_name)) { var schedule = _schedules[schedule_name]; // setup cron tasks which updates the schedule dev status at schedule // interval beginings and ends addScheduleDevCronTasks(schedule); // if needed, setup periodic task to trigger rules which use this schedule if (schedule.autoUpdate) { addScheduleAutoUpdCronTask(schedule); } // set schedule dev status as soon as possible at startup (function(schedule) { setTimeout(function() { updateSingleScheduleDevStatus(schedule); }, 1); })(schedule); }; }; }; })(Schedules); </syntaxhighlight> An example of a rule using Schedules: <syntaxhighlight lang="ecmascript"> (function() { // closing defineAlias("countersTemperature", "wb-msw2_30/Temperature"); defineAlias("vegetablesTemperature", "wb-msw2_31/Temperature"); defineAlias("heater1EnableInverted", "wb-mrm2-old_70/Relay 1"); defineAlias("frontshopVentInverted", "wb-gpio/EXT1_R3A3"); Schedules.registerSchedule({ "name" : "signboard", // signboard "autoUpdate" : "1m", "intervals" : [ [ [12, 30], [20, 30] ], // in UTC, 15:30 - 23:30 MSK [ [3, 30], [5, 20] ], // in UTC, 6:30 - 8:20 MSK ] }); Schedules.registerSchedule({ "name" : "ext_working_hours_15m", "autoUpdate" : "1m", "intervals" : [ [ [4, 45], [20, 15] ], // still UTC, 07:45 - 23:15 MSK ] }); Schedules.registerSchedule({ "name" : "working_hours", "autoUpdate" : "1m", "intervals" : [ [ [5, 0], [19, 0] ], // still UTC, 8:00 - 22:00 MSK ] }); Schedules.registerSchedule({ "name" : "working_hours_15m", "autoUpdate" : "1m", "intervals" : [ [ [4, 45], [19, 15] ], // still UTC, 7:45 - 22:15 MSK ] }); Schedules.registerSchedule({ "name" : "frontshop_lighting", "autoUpdate" : "1m", "intervals" : [ [ [4, 20], [20, 45] ], // still UTC, 7:20 -23:45 MSK ] }); Schedules.registerSchedule({ "name" : "heaters_schedule", "intervals" : [ [ [4, 0], [17, 0] ], // still UTC, 07:00 - 20:00 MSK дневной режим ] }); Schedules.initSchedules(); // signboard and facade illumination defineRule("signboardOnOff", { when: function() { return dev._schedules.signboard || true; }, then: function (newValue, devName, cellName) { log("signboardOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); var on = dev._schedules.signboard; // dev["wb-mr6c_80/K2"] = !on; dev["wb-mr6c_80/K1"] = !on; dev["wb-mr6c_80/K3"] = !on; } }); // sales area illumination defineRule("lightingFrontshopOnOff", { when: function() { return dev._schedules.frontshop_lighting || true; }, then: function (newValue, devName, cellName) { log("lightingFrontshopOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); dev["wb-gpio/EXT1_R3A1"] = ! dev._schedules.frontshop_lighting; //inverted contactor } }); // backstoreroom ventilation defineRule("ventBackstoreOnOff", { when: function() { return dev._schedules.ext_working_hours_15m || true; }, then: function (newValue, devName, cellName) { log("ventBackstoreOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); var on = dev._schedules.ext_working_hours_15m; dev["wb-mr6c_81/K1"] = ! on; //inverted contactor dev["wb-mr6c_81/K5"] = ! on; //inverted contactor } }); // Freezer showcase illumination defineRule("lightingCoolingshelfsOnOff", { when: function() { return dev._schedules.frontshop_lighting || true; }, then: function (newValue, devName, cellName) { log("lightingCoolingshelfsOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); var on = dev._schedules.working_hours_15m; // the lighting in the freezer showcases via the normally-closed relays (inverted) dev["wb-mrm2-old_60/Relay 1"] = !on; dev["wb-mrm2-old_61/Relay 1"] = !on; dev["wb-mrm2-old_62/Relay 1"] = !on; dev["wb-mrm2-old_63/Relay 1"] = !on; dev["wb-mrm2-old_64/Relay 1"] = !on; dev["wb-mrm2-old_65/Relay 1"] = !on; dev["wb-mrm2-old_66/Relay 1"] = !on; dev["wb-mrm2-old_67/Relay 1"] = !on; } }); //Display fridges defineRule("powerBrandFridgesOnOff", { when: function() { return dev._schedules.working_hours || true; }, then: function (newValue, devName, cellName) { log("powerBrandFridgesOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); var on = dev._schedules.working_hours; dev["wb-gpio/EXT1_R3A5"] = !on; // inverted } }); // ========= Boilers and supply ventilation ТЗ =========== // feedback on the temperature of the vegetable zone // position controller works daily defineRule("heatersDayOff", { when: function() { return (dev._schedules.heaters_schedule) && (vegetablesTemperature > 17.0); }, then: function (newValue, devName, cellName) { log("heatersDayOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); heater1EnableInverted = !false; // inverted } }); defineRule("heatersDayOn", { when: function() { return (dev._schedules.heaters_schedule) && (vegetablesTemperature < 16.7); }, then: function (newValue, devName, cellName) { log("heatersDayOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); heater1EnableInverted = !true; // inverted } }); // position controller works at night defineRule("heatersNightOff", { when: function() { return (!dev._schedules.heaters_schedule) && (vegetablesTemperature > 11.6); }, then: function (newValue, devName, cellName) { log("heatersNightOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); heater1EnableInverted = !false; // inverted } }); defineRule("heatersNightOn", { when: function() { return (!dev._schedules.heaters_schedule) && (vegetablesTemperature < 11.3); }, then: function (newValue, devName, cellName) { log("heatersNightOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); heater1EnableInverted = !true; // inverted } }); // supply and exhaust ventilation are forcibly switched off defineRule("ventFrontshopAlwaysOff", { when: cron("@every 1m"), then: function() { dev["wb-gpio/EXT1_R3A3"] = !false; dev["wb-gpio/EXT1_R3A4"] = !false; } }); // ================== The cash register area ================= // in the checkout area during working hours, the temperature is maintained by air conditioners (position controller) defineRule("countersACOn", { when: function() { return (dev._schedules.working_hours_15m) && (countersTemperature < 17.7); }, then: function (newValue, devName, cellName) { log("countersACOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); dev["wb-mir_75/Play from ROM7"] = true; // air conditioning cash area for heating } }); // after working hours, the air conditioning is off defineRule("countersACOff", { when: function() { return (!dev._schedules.working_hours_15m) || (countersTemperature > 18.0); }, then: function (newValue, devName, cellName) { log("countersACOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); dev["wb-mir_75/Play from ROM2"] = true; // shut down air conditioning cash area } }); // =============== Vegetable Zone ============== // Refrigeration of vegetables with air conditioning only when the air temperature is above 18.5C defineRule("acVegOn", { when: function() { return vegetablesTemperature >= 18.5 }, then: function (newValue, devName, cellName) { log("acVegOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); dev["wb-mir_76/Play from ROM3"] = true; // Cooling +18 } }); defineRule("acVegOff", { when: function() { return vegetablesTemperature < 17.8 }, then: function (newValue, devName, cellName) { log("acVegOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); dev["wb-mir_76/Play from ROM2"] = true; // turn off } }); })() </syntaxhighlight> == Полезные ссылки == * [[Wb-rules | Brief description of wb-rules on wiki]] * [https://github.com/wirenboard/wb-rules Full description of wb-rules on Github]