wb_editors
14 457
правок
Matveevrj (обсуждение | вклад) (Новая страница: «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"); } }); })();») |
FuzzyBot (обсуждение | вклад) (Обновление для соответствия новой версии исходной страницы.) |
||
(не показано 76 промежуточных версий 2 участников) | |||
Строка 1: | Строка 1: | ||
<languages/> | <languages/> | ||
{{DISPLAYTITLE: Rule Examples}} | |||
=== Control tracking === | === Control tracking === | ||
Строка 7: | Строка 8: | ||
For example, a rule can turn on a siren and a lamp if a motion sensor detects movement. | 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 | 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 | 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"> | <syntaxhighlight lang="ecmascript"> | ||
Строка 16: | Строка 17: | ||
whenChanged: "wb-gpio/D1_IN", | whenChanged: "wb-gpio/D1_IN", | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
dev["wb-gpio | dev["wb-gpio/Relay_2"] = newValue; | ||
dev["wb-mrm2_6 | dev["wb-mrm2_6/Relay 1"] = newValue; | ||
} | } | ||
Строка 40: | Строка 41: | ||
whenChanged: "simple_test/enabled", | whenChanged: "simple_test/enabled", | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
dev["wb-gpio | dev["wb-gpio/Relay_2"] = newValue; | ||
dev["wb-mrm2_6 | dev["wb-mrm2_6/Relay 1"] = newValue; | ||
} | } | ||
Строка 49: | Строка 50: | ||
=== Motion detection with timeout === | === Motion detection with timeout === | ||
A motion detector with a | 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 lighting is connected via a built-in relay corresponding to the <code>wb-gpio/Relay_1</code> channel. | ||
The rule works like this: | The rule works like this: | ||
* when movement appears, the light turns on. If a thirty-second | * 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 | * 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"> | <syntaxhighlight lang="ecmascript"> | ||
Строка 65: | Строка 66: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
if (newValue) { | if (newValue) { | ||
dev["wb-gpio | 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 | 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); | ||
Строка 89: | Строка 90: | ||
then: function(newValue, devName, cellName) { | then: function(newValue, devName, cellName) { | ||
if (!newValue) { | if (!newValue) { | ||
dev["wb-gpio" | dev["wb-gpio/relay_control"] = true; | ||
if (motion_timer_id) { | if (motion_timer_id) { | ||
clearTimeout(motion_timer_id); | clearTimeout(motion_timer_id); | ||
Строка 95: | Строка 96: | ||
motion_timer_id = setTimeout(function() { | motion_timer_id = setTimeout(function() { | ||
dev["wb-gpio" | dev["wb-gpio/relay_control"] = false; | ||
motion_timer_id = null; | motion_timer_id = null; | ||
}, timeout_ms); | }, timeout_ms); | ||
Строка 135: | Строка 136: | ||
if ((date > date_start) && (date < date_end)) { | if ((date > date_start) && (date < date_end)) { | ||
if (newValue) { | if (newValue) { | ||
dev["wb-gpio | dev["wb-gpio/EXT1_R3A1"] = 1; | ||
if (motion_timer_1_id) { | if (motion_timer_1_id) { | ||
Строка 142: | Строка 143: | ||
motion_timer_1_id = setTimeout(function () { | motion_timer_1_id = setTimeout(function () { | ||
dev["wb-gpio | 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); | ||
Строка 179: | Строка 180: | ||
defineRule( "roller_shutter_up_on" + suffix, { | defineRule( "roller_shutter_up_on" + suffix, { | ||
asSoonAs: function() { | asSoonAs: function() { | ||
return dev[relay_up_device | return dev["relay_up_device/relay_up_control"]; | ||
}, | }, | ||
then: function () { | then: function () { | ||
Строка 187: | Строка 188: | ||
relay_up_timer_id = setTimeout(function() { | relay_up_timer_id = setTimeout(function() { | ||
return dev[relay_up_device | return dev["relay_up_device/relay_up_control"] = 0; | ||
}, timeout_s * 1000); | }, timeout_s * 1000); | ||
} | } | ||
Строка 202: | Строка 203: | ||
relay_down_timer_id = setTimeout(function() { | relay_down_timer_id = setTimeout(function() { | ||
dev[relay_down_device | dev["relay_down_device/relay_down_control"] = 0; | ||
}, timeout_s * 1000); | }, timeout_s * 1000); | ||
} | } | ||
Строка 209: | Строка 210: | ||
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 | return dev[relay_up_device][relay_up_control] && dev["relay_down_device/relay_down_control"]; | ||
}, | }, | ||
then: function () { | then: function () { | ||
Строка 222: | Строка 223: | ||
dev[relay_up_device][relay_up_control] = 0; | dev[relay_up_device][relay_up_control] = 0; | ||
dev[relay_down_device | 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"); | ||
} | } | ||
Строка 281: | Строка 282: | ||
var inpulseValue = 10 // Number of liters per impulse | var inpulseValue = 10 // Number of liters per impulse | ||
defineVirtualDevice("water_meters", { // | |||
defineVirtualDevice("water_meters", { // | We create a virtual device for display in the web interface. | ||
title: " | title: "Water meters", | ||
cells: { | cells: { | ||
water_meter_1: { | water_meter_1: { | ||
Строка 291: | Строка 292: | ||
} | } | ||
}); | }); | ||
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 | 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> | </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 | {{YouTube | ||
|link=https://youtu.be/C60KB7TCeKg | |link=https://youtu.be/C60KB7TCeKg | ||
|text= | |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"> | <syntaxhighlight lang="js"> | ||
/* ---------------------------- */ | /* ---------------------------- */ | ||
/* 1. Single Press Counter: On action*/ | /* 1. Single Press Counter: On action*/ | ||
/* ---------------------------- */ | /* ---------------------------- */ | ||
defineRule({ | defineRule({ | ||
whenChanged: "wb-mcm8_20/Input 1 Single Press Counter", | whenChanged: "wb-mcm8_20/Input 1 Single Press Counter", | ||
Строка 351: | Строка 336: | ||
} | } | ||
}); | }); | ||
/* ---------------------------- */ | /* ---------------------------- */ | ||
/* 2. Double Press Counter: Off action*/ | /* 2. Double Press Counter: Off action*/ | ||
/* ---------------------------- */ | /* ---------------------------- */ | ||
defineRule({ | defineRule({ | ||
whenChanged: "wb-mcm8_20/Input 1 Double Press Counter", | whenChanged: "wb-mcm8_20/Input 1 Double Press Counter", | ||
Строка 367: | Строка 348: | ||
} | } | ||
}); | }); | ||
/* --------------------------------------- */ | /* --------------------------------------- */ | ||
/* 3. Long Press Counter: Increase brightness */ | /* 3. Long Press Counter: Increase brightness */ | ||
/* --------------------------------------- */ | /* --------------------------------------- */ | ||
defineRule({ | defineRule({ | ||
whenChanged: "wb-mcm8_20/Input 1 Long Press Counter", | whenChanged: "wb-mcm8_20/Input 1 Long Press Counter", | ||
Строка 384: | Строка 361: | ||
} | } | ||
}); | }); | ||
// A rule that will increase the brightness on a timer | // A rule that will increase the brightness on a timer | ||
defineRule({ | defineRule({ | ||
Строка 392: | Строка 367: | ||
then: function () { | then: function () { | ||
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"]) { | |||
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 | ||
Строка 403: | Строка 376: | ||
} | } | ||
}); | }); | ||
/* -------------------------------------------- */ | /* -------------------------------------------- */ | ||
/* 4. Shortlong Press Counter: Decrease brightness */ | /* 4. Shortlong Press Counter: Decrease brightness */ | ||
/* -------------------------------------------- */ | /* -------------------------------------------- */ | ||
defineRule({ | defineRule({ | ||
whenChanged: "wb-mcm8_20/Input 1 Shortlong Press Counter", | whenChanged: "wb-mcm8_20/Input 1 Shortlong Press Counter", | ||
Строка 420: | Строка 389: | ||
} | } | ||
}); | }); | ||
// A rule that will decrease the brightness on a timer | // A rule that will decrease the brightness on a timer | ||
defineRule({ | defineRule({ | ||
Строка 428: | Строка 395: | ||
then: function () { | then: function () { | ||
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"]) { | |||
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 | ||
Строка 439: | Строка 404: | ||
} | } | ||
}); | }); | ||
</syntaxhighlight> | </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 === | === 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;"> | <div class="mw-collapsible mw-collapsed"; style="width:600px; overflow: hidden;"> | ||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
Строка 477: | Строка 428: | ||
var co2_middle = newValue < 1000 && newValue > 651; | var co2_middle = newValue < 1000 && newValue > 651; | ||
var co2_bad = newValue > 1001; | var co2_bad = newValue > 1001; | ||
if (co2_good) { | |||
if (co2_good) { | dev["devName/Green LED"] = true; | ||
dev[devName | dev["devName/Red LED"] = false; | ||
dev[devName | dev["devName/LED Period (s)"] = 10; | ||
dev[devName | |||
} | } | ||
if (co2_middle) { | if (co2_middle) { | ||
dev[devName | dev["devName/Green LED"] = true; | ||
dev[devName | dev["devName/Red LED"] = true; | ||
dev[devName | dev["devName/LED Period (s)"] = 5; | ||
} | } | ||
if (co2_bad) { | if (co2_bad) { | ||
dev[devName | dev["devName/Green LED"] = false; | ||
dev[devName | dev["devName/Red LED"] = true; | ||
dev[devName | dev["devName/LED Period (s)"] = 1; | ||
} | } | ||
} | } | ||
Строка 500: | Строка 449: | ||
</div> | </div> | ||
=== Max Motion === | === Max Motion === | ||
"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="NavFrame"> | ||
<div class="NavContent"> | <div class="NavContent"> | ||
Строка 511: | Строка 458: | ||
then: function(newValue, devName, cellName) { | then: function(newValue, devName, cellName) { | ||
if (newValue > 50) { | if (newValue > 50) { | ||
if (dev["wb-msw-v3_97 | if (dev["wb-msw-v3_97/Illuminance"] < 50) { | ||
dev["wb-mr3_11 | dev["wb-mr3_11/K1"] = true; | ||
} | } | ||
} else { | } else { | ||
dev["wb-mr3_11 | dev["wb-mr3_11/K1"] = false; | ||
} | } | ||
} | } | ||
}); | }); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</div> | </div> | ||
</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. | |||
[https://github.com/contactless/wb-rules-system/blob/master/rules/buzzer.js | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
defineVirtualDevice("buzzer", { | defineVirtualDevice("buzzer", { | ||
title: "Buzzer", // | title: "Buzzer", // | ||
cells: { | |||
cells: { | |||
frequency : { | frequency : { | ||
type : "range", | type : "range", | ||
Строка 568: | Строка 501: | ||
} | } | ||
}); | }); | ||
// setup pwm2 | // setup pwm2 | ||
runShellCommand("echo 2 > /sys/class/pwm/pwmchip0/export"); | runShellCommand("echo 2 > /sys/class/pwm/pwmchip0/export"); | ||
function _buzzer_set_params() { | function _buzzer_set_params() { | ||
var period = parseInt(1.0 / dev.buzzer.frequency * 1E9); | var period = parseInt(1.0 / dev.buzzer.frequency * 1E9); | ||
var duty_cycle = parseInt(dev.buzzer.volume * 1.0 / 100 * period * 0.5); | var duty_cycle = parseInt(dev.buzzer.volume * 1.0 / 100 * period * 0.5); | ||
runShellCommand("echo " + period + " > /sys/class/pwm/pwmchip0/pwm2/period"); | |||
runShellCommand("echo " + period + " > /sys/class/pwm/pwmchip0/pwm2/period"); | |||
runShellCommand("echo " + duty_cycle + " > /sys/class/pwm/pwmchip0/pwm2/duty_cycle"); | runShellCommand("echo " + duty_cycle + " > /sys/class/pwm/pwmchip0/pwm2/duty_cycle"); | ||
}; | }; | ||
defineRule("_system_buzzer_params", { | defineRule("_system_buzzer_params", { | ||
whenChanged: [ | whenChanged: [ | ||
Строка 596: | Строка 521: | ||
"buzzer/volume", | "buzzer/volume", | ||
], | ], | ||
then: function (newValue, devName, cellName) { | |||
then: function (newValue, devName, cellName) { | |||
if ( dev.buzzer.enabled) { | if ( dev.buzzer.enabled) { | ||
_buzzer_set_params(); | _buzzer_set_params(); | ||
Строка 605: | Строка 528: | ||
} | } | ||
}); | }); | ||
defineRule("_system_buzzer_onof", { | defineRule("_system_buzzer_onof", { | ||
whenChanged: "buzzer/enabled", | whenChanged: "buzzer/enabled", | ||
Строка 620: | Строка 541: | ||
} | } | ||
}); | }); | ||
</syntaxhighlight> | </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. | |||
[https://github.com/contactless/wb-rules-system/blob/master/rules/power_status.js | |||
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. | |||
1. | |||
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. | |||
2. | |||
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"> | <syntaxhighlight lang="ecmascript"> | ||
defineVirtualDevice("power_status", { | defineVirtualDevice("power_status", { | ||
title: "Power status", // | title: "Power status", // | ||
cells: { | |||
cells: { | |||
'working on battery' : { | 'working on battery' : { | ||
type : "switch", | type : "switch", | ||
Строка 671: | Строка 572: | ||
value : 0 | value : 0 | ||
} | } | ||
} | |||
} | |||
}); | }); | ||
defineRule("_system_track_vin", { | defineRule("_system_track_vin", { | ||
whenChanged: "wb-adc/Vin", | whenChanged: "wb-adc/Vin", | ||
then: function() { | then: function() { | ||
if (dev["wb-adc | if (dev["wb-adc/Vin"] < dev["wb-adc/BAT"] ) { | ||
dev["power_status | dev["power_status/Vin"] = 0; | ||
} else { | } else { | ||
dev["power_status | dev["power_status/Vin"] = dev["wb-adc/Vin"] ; | ||
} | } | ||
} | } | ||
}); | }); | ||
defineRule("_system_dc_on", { | defineRule("_system_dc_on", { | ||
asSoonAs: function () { | asSoonAs: function () { | ||
return dev["wb-adc | return dev["wb-adc/Vin"] > dev["wb-adc/BAT"]; | ||
}, | }, | ||
then: function () { | then: function () { | ||
dev["power_status | dev["power_status/working on battery"] = false; | ||
} | } | ||
}); | }); | ||
defineRule("_system_dc_off", { | defineRule("_system_dc_off", { | ||
asSoonAs: function () { | asSoonAs: function () { | ||
return dev["wb-adc | return dev["wb-adc/Vin"] <= dev["wb-adc/BAT"]; | ||
}, | }, | ||
then: function () { | then: function () { | ||
dev["power_status | dev["power_status/working on battery"] = true; | ||
} | } | ||
}); | }); | ||
</syntaxhighlight> | </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"> | <syntaxhighlight lang="js"> | ||
defineVirtualDevice("Termostat", { | defineVirtualDevice("Termostat", { | ||
title: "Termostat", | title: "Termostat", | ||
cells: { | cells: { | ||
// =============== | // =============== hallway underfloor heating | ||
"R01-TS16-1-mode": {// | "R01-TS16-1-mode": {//mode 0-manual 1-scheduled | ||
type: "switch", | type: "switch", | ||
value: false, | value: false, | ||
}, | }, | ||
"R01-TS16-1-setpoint": {// | "R01-TS16-1-setpoint": {//setting | ||
type: "range", | type: "range", | ||
value: 25, | value: 25, | ||
Строка 743: | Строка 630: | ||
readonly: false | readonly: false | ||
}, | }, | ||
"R01-TS16-1-lock": {// | "R01-TS16-1-lock": {//blockage in visualization 0-unlocked 1-blocked | ||
type: "switch", | type: "switch", | ||
value: false, | value: false, | ||
},....... | },....... | ||
var hysteresis = 0.5; | var hysteresis = 0.5; | ||
function Termostat(name, temp, setpoint, TS, TS_onoff) { | function Termostat(name, temp, setpoint, TS, TS_onoff) { | ||
defineRule(name, { | defineRule(name, { | ||
whenChanged: temp, // | whenChanged: temp, //when the sensor state changes | ||
then: function (newValue, devName, cellName) { // | then: function (newValue, devName, cellName) { // do the following | ||
if (dev[TS_onoff]) { | 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; | else dev[TS] = false; | ||
Строка 767: | Строка 652: | ||
}); | }); | ||
} | } | ||
Termostat("R01-TS16-1", "A60-M1W3/External Sensor 1", "Termostat/R01-TS16-1-setpoint", "wb-gpio/EXT4_R3A1", "Termostat/R01-TS16-1-onoff"); // | |||
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> | </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> | <pre> | ||
FF FF 0A 01 FF 00 00 0A | FF FF 0A 01 FF 00 00 0A | ||
</pre> | </pre> | ||
When you turn off the switch a command will be sent: (set channel brightness 00=0x00) for Uniel UCH-M141: | |||
<pre> | <pre> | ||
FF FF 0A 01 00 00 00 0B | FF FF 0A 01 00 00 00 0B | ||
</pre> | </pre> | ||
1. Port setting | |||
1. | |||
To configure the / dev/ttyNSC0 port to 9600 speed, run the following command | |||
<pre> | <pre> | ||
stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8 | stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8 | ||
</pre> | </pre> | ||
2. Sending a command | |||
2. | |||
Sending data is done with the following shell command: | |||
<pre> | <pre> | ||
/usr/bin/printf '\xFF\xFF\x0A\x01\xD1\x06\x00\xE2' >/dev/ttyNSC0 | /usr/bin/printf '\xFF\xFF\x0A\x01\xD1\x06\x00\xE2' >/dev/ttyNSC0 | ||
</pre> | </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 | |||
3. | |||
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> | <pre> | ||
root@wirenboard:~# mcedit /etc/wb-rules/rs485_cmd.js | root@wirenboard:~# mcedit /etc/wb-rules/rs485_cmd.js | ||
</pre> | </pre> | ||
4. Describe the virtual device in the file | |||
4. | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
defineVirtualDevice("rs485_cmd", { | defineVirtualDevice("rs485_cmd", { | ||
Строка 863: | Строка 714: | ||
}); | }); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
5. Restart wb-rules and check the operation | |||
5. | |||
<pre> | <pre> | ||
root@wirenboard:~# /etc/init.d/wb-rules restart | root@wirenboard:~# /etc/init.d/wb-rules restart | ||
root@wirenboard:~# tail -f /var/log/messages | root@wirenboard:~# tail -f /var/log/messages | ||
</pre> | </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. | |||
6. | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
function setup_port() { | function setup_port() { | ||
runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8"); | runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8"); | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
7. Let's describe the rules for turning the switch on and off | |||
7. | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
defineRule("_rs485_switch_on", { | defineRule("_rs485_switch_on", { | ||
Строка 913: | Строка 746: | ||
} | } | ||
}); | }); | ||
defineRule("_rs485_switch_off", { | defineRule("_rs485_switch_off", { | ||
asSoonAs: function () { | asSoonAs: function () { | ||
Строка 924: | Строка 755: | ||
} | } | ||
}); | }); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Note the double shielding. | |||
7. Putting it all together | |||
7. | |||
The full contents of the file with the rules: | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
defineVirtualDevice("rs485_cmd", { | defineVirtualDevice("rs485_cmd", { | ||
Строка 953: | Строка 774: | ||
} | } | ||
}); | }); | ||
function setup_port() { | function setup_port() { | ||
runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8"); | runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8"); | ||
} | } | ||
defineRule("_rs485_switch_on", { | defineRule("_rs485_switch_on", { | ||
asSoonAs: function () { | asSoonAs: function () { | ||
Строка 970: | Строка 787: | ||
} | } | ||
}); | }); | ||
defineRule("_rs485_switch_off", { | defineRule("_rs485_switch_off", { | ||
asSoonAs: function () { | asSoonAs: function () { | ||
Строка 981: | Строка 796: | ||
} | } | ||
}); | }); | ||
setTimeout(setup_port, 1000); // set setup_port() running 1 second after starting. | |||
setTimeout(setup_port, 1000); // | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== Sending a message via Telegram bot == | |||
{{Anchor|telegram}} | {{Anchor|telegram}} | ||
Messages are sent using [https://core.telegram.org/api#telegram-api Telegram API] via <code>curl</code>. | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
varmessage = "Text"; // write your message text | |||
var token = "TOKEN"; // | var token = "TOKEN"; // replace with bot token | ||
var chat_id = CHATID; // | 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); | var command = 'curl -s -X POST https://api.telegram.org/bot{}/sendMessage -d chat_id={} -d text="{}"'.format(token, chat_id, message); | ||
runShellCommand(command); | runShellCommand(command); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Handling errors when working with serial devices == | |||
Implemented by subscribing to all '''meta/error''' topics. | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
defineVirtualDevice("meta_error_test", { | defineVirtualDevice("meta_error_test", { | ||
Строка 1032: | Строка 833: | ||
} | } | ||
}); | }); | ||
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)) | ||
Строка 1045: | Строка 844: | ||
}); | }); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Custom fields in web interface == | |||
= | |||
[[File:Sample-custom-config-1.png|300px|thumb|right|Example configuration]] | |||
[[File:Sample-custom-config-1.png|300px|thumb|right| | [[File:Sample-custom-config-2.png|300px|thumb|right|Example script]] | ||
[[File:Sample-custom-config-2.png|300px|thumb|right| | 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 | |||
1. | section: /usr/share/wb-mqtt-confed/schemas/test-config.schema.json | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
{ | { | ||
Строка 1078: | Строка 866: | ||
"title":"Test configuration", | "title":"Test configuration", | ||
"description":"Long description configuration", | "description":"Long description configuration", | ||
"configFile": { | "configFile": { | ||
"path":"/etc/test-config.conf", | "path":"/etc/test-config.conf", | ||
"service":"wb-rules" | "service":"wb-rules" | ||
}, | }, | ||
"properties": { | "properties": { | ||
"temperature_setpoint": { | "temperature_setpoint": { | ||
Строка 1097: | Строка 881: | ||
"maximum": 40 | "maximum": 40 | ||
}, | }, | ||
"humidity_setpoint": { | "humidity_setpoint": { | ||
"type":"number", | "type":"number", | ||
Строка 1112: | Строка 894: | ||
} | } | ||
</syntaxhighlight> | </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 | |||
2. | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
{ | { | ||
Строка 1122: | Строка 902: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
3. Script that updates config : /mnt/data/etc/wb-rules/test-config-script.js | |||
3. | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
var config = readConfig("/etc/test-config.conf"); | var config = readConfig("/etc/test-config.conf"); | ||
log("temperature setpoint is: {}".format(config.temperature_setpoint)); | log("temperature setpoint is: {}".format(config.temperature_setpoint)); | ||
</syntaxhighlight> | </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"> | <syntaxhighlight lang="bash"> | ||
service wb-mqtt-confed restart | service wb-mqtt-confed restart | ||
service wb-rules restart | service wb-rules restart | ||
</syntaxhighlight> | </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: | lib_schedules.js: | ||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
global.__proto__.Schedules = {}; | global.__proto__.Schedules = {}; | ||
(function(Schedules) { // closing | |||
(function(Schedules) { // | |||
function todayAt(now, hours, minutes) { | |||
function todayAt(now, hours, minutes) { | |||
var date = new Date(now); | var date = new Date(now); | ||
// i.e. "today, at HH:MM". All dates are in UTC! | // i.e. "today, at HH:MM". All dates are in UTC! | ||
Строка 1205: | Строка 949: | ||
return date; | return date; | ||
} | } | ||
function checkScheduleInterval(now, start_time, end_time) { | |||
function checkScheduleInterval(now, start_time, end_time) { | |||
var start_date = todayAt(now, start_time[0], start_time[1]); | var start_date = todayAt(now, start_time[0], start_time[1]); | ||
var end_date = todayAt(now, end_time[0], end_time[1]); | var end_date = todayAt(now, end_time[0], end_time[1]); | ||
log("checkScheduleInterval {} {} {}".format(now, start_date, end_date)); | log("checkScheduleInterval {} {} {}".format(now, start_date, end_date)); | ||
if (end_date >= start_date) { | |||
if (end_date >= start_date) { | |||
if ((now >= start_date) && (now < end_date)) { | if ((now >= start_date) && (now < end_date)) { | ||
return true; | return true; | ||
Строка 1222: | Строка 962: | ||
// end date is less than start date, | // end date is less than start date, | ||
// assuming they belong to a different days (e.g. today and tomorrow) | // assuming they belong to a different days (e.g. today and tomorrow) | ||
// option 1: what if it's now the day of "end" date? | |||
// option 1: what if it's now the day of "end" date? | |||
// in this case the following is enough: | // in this case the following is enough: | ||
if (now < end_date) { | if (now < end_date) { | ||
return true; | return true; | ||
} | } | ||
// well, that seems not to be the case. ok, | |||
// well, that seems not to be the case. ok, | |||
// option 2: it's the day of "start" date: | // option 2: it's the day of "start" date: | ||
if (now >= start_date) { | |||
if (now >= start_date) { | |||
return true; | return true; | ||
} | } | ||
} | } | ||
return false; | return false; | ||
} | |||
} | |||
function checkSchedule(schedule, now) { | |||
function checkSchedule(schedule, now) { | |||
if (now == undefined) { | if (now == undefined) { | ||
now = new Date(); | now = new Date(); | ||
} | } | ||
for (var i = 0; i < schedule.intervals.length; ++i) { | |||
for (var i = 0; i < schedule.intervals.length; ++i) { | |||
var item = schedule.intervals[i]; | var item = schedule.intervals[i]; | ||
if (checkScheduleInterval(now, item[0], item[1])) { | if (checkScheduleInterval(now, item[0], item[1])) { | ||
Строка 1271: | Строка 999: | ||
dev["_schedules"][schedule.name] = checkSchedule(schedule); | dev["_schedules"][schedule.name] = checkSchedule(schedule); | ||
}; | }; | ||
function addScheduleDevCronTasks(schedule) { | |||
function addScheduleDevCronTasks(schedule) { | |||
for (var i = 0; i < schedule.intervals.length; ++i) { | for (var i = 0; i < schedule.intervals.length; ++i) { | ||
var interval = schedule.intervals[i]; | var interval = schedule.intervals[i]; | ||
Строка 1291: | Строка 1017: | ||
} | } | ||
} | } | ||
function addScheduleAutoUpdCronTask(schedule) { | |||
function addScheduleAutoUpdCronTask(schedule) { | |||
defineRule("_schedule_auto_upd_{}".format(schedule.name), { | defineRule("_schedule_auto_upd_{}".format(schedule.name), { | ||
when: cron("@every " + schedule.autoUpdate), | when: cron("@every " + schedule.autoUpdate), | ||
Строка 1302: | Строка 1026: | ||
}); | }); | ||
} | } | ||
var _schedules = {}; | |||
var _schedules = {}; | |||
Schedules.registerSchedule = function(schedule) { | |||
Schedules.registerSchedule = function(schedule) { | |||
_schedules[schedule.name] = schedule; | _schedules[schedule.name] = schedule; | ||
}; | }; | ||
Schedules.initSchedules = function() { | |||
Schedules.initSchedules = function() { | |||
var params = { | var params = { | ||
title: "Schedule Status", | title: "Schedule Status", | ||
cells: {} | cells: {} | ||
}; | }; | ||
for (var schedule_name in _schedules) { | |||
for (var schedule_name in _schedules) { | |||
if (_schedules.hasOwnProperty(schedule_name)) { | if (_schedules.hasOwnProperty(schedule_name)) { | ||
var schedule = _schedules[schedule_name]; | var schedule = _schedules[schedule_name]; | ||
Строка 1329: | Строка 1045: | ||
} | } | ||
}; | }; | ||
defineVirtualDevice("_schedules", params); | |||
defineVirtualDevice("_schedules", params); | |||
for (var schedule_name in _schedules) { | |||
for (var schedule_name in _schedules) { | |||
if (_schedules.hasOwnProperty(schedule_name)) { | if (_schedules.hasOwnProperty(schedule_name)) { | ||
var schedule = _schedules[schedule_name]; | var schedule = _schedules[schedule_name]; | ||
// setup cron tasks which updates the schedule dev status at schedule | |||
// setup cron tasks which updates the schedule dev status at schedule | |||
// interval beginings and ends | // interval beginings and ends | ||
addScheduleDevCronTasks(schedule); | addScheduleDevCronTasks(schedule); | ||
// if needed, setup periodic task to trigger rules which use this schedule | |||
// if needed, setup periodic task to trigger rules which use this schedule | |||
if (schedule.autoUpdate) { | if (schedule.autoUpdate) { | ||
addScheduleAutoUpdCronTask(schedule); | addScheduleAutoUpdCronTask(schedule); | ||
} | } | ||
// set schedule dev status as soon as possible at startup | |||
// set schedule dev status as soon as possible at startup | |||
(function(schedule) { | (function(schedule) { | ||
setTimeout(function() { | setTimeout(function() { | ||
Строка 1362: | Строка 1068: | ||
}, 1); | }, 1); | ||
})(schedule); | })(schedule); | ||
}; | |||
}; | |||
}; | }; | ||
}; | }; | ||
})(Schedules); | })(Schedules); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
An example of a rule using Schedules: | |||
<syntaxhighlight lang="ecmascript"> | <syntaxhighlight lang="ecmascript"> | ||
(function() { // | (function() { // closing | ||
defineAlias("countersTemperature", "wb-msw2_30/Temperature"); | |||
defineAlias("countersTemperature", "wb-msw2_30/Temperature"); | |||
defineAlias("vegetablesTemperature", "wb-msw2_31/Temperature"); | defineAlias("vegetablesTemperature", "wb-msw2_31/Temperature"); | ||
defineAlias("heater1EnableInverted", "wb-mrm2-old_70/Relay 1"); | |||
defineAlias("heater1EnableInverted", "wb-mrm2-old_70/Relay 1"); | |||
defineAlias("frontshopVentInverted", "wb-gpio/EXT1_R3A3"); | defineAlias("frontshopVentInverted", "wb-gpio/EXT1_R3A3"); | ||
Schedules.registerSchedule({ | |||
Schedules.registerSchedule({ | "name" : "signboard", // signboard | ||
"name" : "signboard", // | |||
"autoUpdate" : "1m", | "autoUpdate" : "1m", | ||
"intervals" : [ | "intervals" : [ | ||
[ [12, 30], [20, 30] ], // | [ [12, 30], [20, 30] ], // in UTC, 15:30 - 23:30 MSK | ||
[ [3, 30], [5, 20] ], // | [ [3, 30], [5, 20] ], // in UTC, 6:30 - 8:20 MSK | ||
] | ] | ||
}); | }); | ||
Строка 1404: | Строка 1098: | ||
"autoUpdate" : "1m", | "autoUpdate" : "1m", | ||
"intervals" : [ | "intervals" : [ | ||
[ [4, 45], [20, 15] ], // | [ [4, 45], [20, 15] ], // still UTC, 07:45 - 23:15 MSK | ||
] | ] | ||
}); | }); | ||
Строка 1411: | Строка 1105: | ||
"autoUpdate" : "1m", | "autoUpdate" : "1m", | ||
"intervals" : [ | "intervals" : [ | ||
[ [5, 0], [19, 0] ], // | [ [5, 0], [19, 0] ], // still UTC, 8:00 - 22:00 MSK | ||
] | ] | ||
}); | }); | ||
Строка 1418: | Строка 1112: | ||
"autoUpdate" : "1m", | "autoUpdate" : "1m", | ||
"intervals" : [ | "intervals" : [ | ||
[ [4, 45], [19, 15] ], // | [ [4, 45], [19, 15] ], // still UTC, 7:45 - 22:15 MSK | ||
] | ] | ||
}); | }); | ||
Строка 1425: | Строка 1119: | ||
"autoUpdate" : "1m", | "autoUpdate" : "1m", | ||
"intervals" : [ | "intervals" : [ | ||
[ [4, 20], [20, 45] ], // | [ [4, 20], [20, 45] ], // still UTC, 7:20 -23:45 MSK | ||
] | ] | ||
}); | }); | ||
Строка 1431: | Строка 1125: | ||
"name" : "heaters_schedule", | "name" : "heaters_schedule", | ||
"intervals" : [ | "intervals" : [ | ||
[ [4, 0], [17, 0] ], // | [ [4, 0], [17, 0] ], // still UTC, 07:00 - 20:00 MSK дневной режим | ||
] | ] | ||
}); | }); | ||
Schedules.initSchedules(); | Schedules.initSchedules(); | ||
// signboard and facade illumination | |||
// | |||
defineRule("signboardOnOff", { | defineRule("signboardOnOff", { | ||
when: function() { | when: function() { | ||
Строка 1452: | Строка 1144: | ||
} | } | ||
}); | }); | ||
// sales area illumination | |||
// | |||
defineRule("lightingFrontshopOnOff", { | defineRule("lightingFrontshopOnOff", { | ||
when: function() { | when: function() { | ||
Строка 1462: | Строка 1152: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("lightingFrontshopOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("lightingFrontshopOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
dev["wb-gpio/EXT1_R3A1"] = ! dev._schedules.frontshop_lighting; // | dev["wb-gpio/EXT1_R3A1"] = ! dev._schedules.frontshop_lighting; //inverted contactor | ||
} | } | ||
}); | }); | ||
// backstoreroom ventilation | |||
// | |||
defineRule("ventBackstoreOnOff", { | defineRule("ventBackstoreOnOff", { | ||
when: function() { | when: function() { | ||
Строка 1476: | Строка 1164: | ||
log("ventBackstoreOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("ventBackstoreOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
var on = dev._schedules.ext_working_hours_15m; | var on = dev._schedules.ext_working_hours_15m; | ||
dev["wb-mr6c_81/K1"] = ! on; // | dev["wb-mr6c_81/K1"] = ! on; //inverted contactor | ||
dev["wb-mr6c_81/K5"] = ! on; // | dev["wb-mr6c_81/K5"] = ! on; //inverted contactor | ||
} | } | ||
}); | }); | ||
// Freezer showcase illumination | |||
// | |||
defineRule("lightingCoolingshelfsOnOff", { | defineRule("lightingCoolingshelfsOnOff", { | ||
when: function() { | when: function() { | ||
Строка 1491: | Строка 1177: | ||
log("lightingCoolingshelfsOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("lightingCoolingshelfsOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
var on = dev._schedules.working_hours_15m; | 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_60/Relay 1"] = !on; | ||
dev["wb-mrm2-old_61/Relay 1"] = !on; | dev["wb-mrm2-old_61/Relay 1"] = !on; | ||
Строка 1505: | Строка 1190: | ||
} | } | ||
}); | }); | ||
//Display fridges | |||
// | |||
defineRule("powerBrandFridgesOnOff", { | defineRule("powerBrandFridgesOnOff", { | ||
when: function() { | when: function() { | ||
Строка 1517: | Строка 1200: | ||
var on = dev._schedules.working_hours; | var on = dev._schedules.working_hours; | ||
dev["wb-gpio/EXT1_R3A5"] = !on; // | 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", { | defineRule("heatersDayOff", { | ||
when: function() { | when: function() { | ||
Строка 1535: | Строка 1214: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("heatersDayOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("heatersDayOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
heater1EnableInverted = !false; // | heater1EnableInverted = !false; // inverted | ||
} | } | ||
}); | }); | ||
defineRule("heatersDayOn", { | |||
defineRule("heatersDayOn", { | |||
when: function() { | when: function() { | ||
return (dev._schedules.heaters_schedule) && (vegetablesTemperature < 16.7); | return (dev._schedules.heaters_schedule) && (vegetablesTemperature < 16.7); | ||
Строка 1547: | Строка 1224: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("heatersDayOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("heatersDayOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
heater1EnableInverted = !true; // | heater1EnableInverted = !true; // inverted | ||
} | } | ||
}); | }); | ||
// position controller works at night | |||
// | |||
defineRule("heatersNightOff", { | defineRule("heatersNightOff", { | ||
when: function() { | when: function() { | ||
Строка 1560: | Строка 1235: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("heatersNightOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("heatersNightOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
heater1EnableInverted = !false; // | heater1EnableInverted = !false; // inverted | ||
} | } | ||
}); | }); | ||
defineRule("heatersNightOn", { | |||
defineRule("heatersNightOn", { | |||
when: function() { | when: function() { | ||
return (!dev._schedules.heaters_schedule) && (vegetablesTemperature < 11.3); | return (!dev._schedules.heaters_schedule) && (vegetablesTemperature < 11.3); | ||
Строка 1572: | Строка 1245: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("heatersNightOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("heatersNightOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
heater1EnableInverted = !true; // | heater1EnableInverted = !true; // inverted | ||
} | } | ||
}); | }); | ||
// supply and exhaust ventilation are forcibly switched off | |||
// | |||
defineRule("ventFrontshopAlwaysOff", { | defineRule("ventFrontshopAlwaysOff", { | ||
Строка 1588: | Строка 1259: | ||
}); | }); | ||
// ================== | |||
// ================== The cash register area ================= | |||
// in the checkout area during working hours, the temperature is maintained by air conditioners (position controller) | |||
// | |||
defineRule("countersACOn", { | |||
defineRule("countersACOn", { | |||
when: function() { | when: function() { | ||
return (dev._schedules.working_hours_15m) && (countersTemperature < 17.7); | return (dev._schedules.working_hours_15m) && (countersTemperature < 17.7); | ||
Строка 1602: | Строка 1270: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("countersACOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("countersACOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
dev["wb-mir_75/Play from ROM7"] = true; // | dev["wb-mir_75/Play from ROM7"] = true; // air conditioning cash area for heating | ||
} | } | ||
}); | }); | ||
// after working hours, the air conditioning is off | |||
// | |||
defineRule("countersACOff", { | defineRule("countersACOff", { | ||
when: function() { | when: function() { | ||
Строка 1615: | Строка 1281: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("countersACOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("countersACOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
dev["wb-mir_75/Play from ROM2"] = true; // | 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", { | |||
defineRule("acVegOn", { | |||
when: function() { | when: function() { | ||
return vegetablesTemperature >= 18.5 | return vegetablesTemperature >= 18.5 | ||
Строка 1632: | Строка 1294: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("acVegOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("acVegOn newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
dev["wb-mir_76/Play from ROM3"] = true; // | dev["wb-mir_76/Play from ROM3"] = true; // Cooling +18 | ||
} | } | ||
}); | }); | ||
defineRule("acVegOff", { | |||
defineRule("acVegOff", { | |||
when: function() { | when: function() { | ||
return vegetablesTemperature < 17.8 | return vegetablesTemperature < 17.8 | ||
Строка 1644: | Строка 1304: | ||
then: function (newValue, devName, cellName) { | then: function (newValue, devName, cellName) { | ||
log("acVegOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | log("acVegOff newValue={}, devName={}, cellName={}", newValue, devName, cellName); | ||
dev["wb-mir_76/Play from ROM2"] = true; // | dev["wb-mir_76/Play from ROM2"] = true; // turn off | ||
} | } | ||
}); | }); | ||
Строка 1650: | Строка 1310: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Полезные ссылки == | == Полезные ссылки == | ||
* [[Wb-rules | | * [[Wb-rules | Brief description of wb-rules on wiki]] | ||
* [https://github.com/wirenboard/wb-rules | * [https://github.com/wirenboard/wb-rules Full description of wb-rules on Github] | ||