mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-12 09:36:59 +00:00
1222 lines
32 KiB
ReStructuredText
1222 lines
32 KiB
ReStructuredText
Rules
|
|
*****
|
|
|
|
Introduction
|
|
============
|
|
|
|
Along with ESP Easy R108, a new feature was enabled, named Rules.
|
|
Rules can be used to create very simple flows to control devices on your ESP.
|
|
|
|
Enable Rules
|
|
------------
|
|
|
|
To enable rules, :menuselection:`Tools --> Advanced` and check the Rules checkbox.
|
|
|
|
After clicking Submit, you will find a new page added. Here you can start
|
|
experimenting with Rules:
|
|
|
|
[ADD_GUI_PICTURE]
|
|
|
|
The example above shows an experiment with a LED, connected via a resistor of
|
|
1k to GPIO12 and to ground.
|
|
|
|
A virtual switch needs to be created in the "Devices" section to allow the
|
|
reading the state of the LED (on or off). The Device needs to be "Switch Input"
|
|
with the following settings:
|
|
|
|
* Device Name - E1SW1
|
|
* Enabled - Ticked
|
|
* Internal Pull-Up - Ticked
|
|
* 1st GPIO - GPIO-12(D6)
|
|
* Switch Type - Switch
|
|
* Switch Button Type - Normal Switch
|
|
|
|
After rebooting the ESP, the LED will start blinking 10 seconds on then 10 seconds off.
|
|
|
|
Enjoy.
|
|
|
|
|
|
Special Notations
|
|
-----------------
|
|
|
|
* ``[...#...]`` Referring to task variable
|
|
* ``%...%`` Referring to system variable
|
|
* ``{...:...}`` Referring to String conversions
|
|
* Quotes (single, double or back quotes) Marking begin and end of a command parameter
|
|
|
|
|
|
|
|
Syntax
|
|
------
|
|
|
|
The syntax of a rule can be single line:
|
|
|
|
.. code-block:: html
|
|
|
|
on <trigger> do <action> endon
|
|
|
|
or multi-line (need to be closed with an "endon"):
|
|
|
|
.. code-block:: html
|
|
|
|
on <trigger> do
|
|
<action>
|
|
<action>
|
|
<action>
|
|
endon
|
|
|
|
IF/ELSE - IF/ELSEIF/ELSE
|
|
------------------------
|
|
|
|
Also simple if ... else ... statements are possible:
|
|
|
|
.. code-block:: html
|
|
|
|
on <trigger> do
|
|
if <test>
|
|
<action>
|
|
<action>
|
|
else
|
|
<action>
|
|
endif
|
|
endon
|
|
|
|
If the "else" part is not needed it can be removed:
|
|
|
|
.. code-block:: html
|
|
|
|
on <trigger> do
|
|
if <test>
|
|
<action>
|
|
<action>
|
|
endif
|
|
endon
|
|
|
|
Also more complex if ... elseif ... else statements are possible (multiple elseif's can be used)
|
|
|
|
.. code-block:: html
|
|
|
|
on <trigger> do
|
|
if <test1>
|
|
<action>
|
|
<action>
|
|
elseif <test2>
|
|
<action>
|
|
<action>
|
|
else
|
|
<action>
|
|
endif
|
|
endon
|
|
|
|
Again, if the "else" part is not needed it can be removed:
|
|
|
|
.. code-block:: html
|
|
|
|
on <trigger> do
|
|
if <test1>
|
|
<action>
|
|
<action>
|
|
elseif <test2>
|
|
<action>
|
|
<action>
|
|
elseif <test3>
|
|
<action>
|
|
<action>
|
|
endif
|
|
endon
|
|
|
|
AND/OR
|
|
------
|
|
|
|
Only simple if/else was possible in older versions, there was this workaround
|
|
for the limitation of not being able to nest. An "event" can be called from a
|
|
"trigger". This possibility of nesting events is also limited , due to its
|
|
consumption of stack space (IRAM). Depending on plug-ins in use this might
|
|
lead to unpredictable, unreliable behavior, advice is not to exceed 3 levels
|
|
of nesting.
|
|
|
|
.. code-block:: html
|
|
|
|
on <trigger> do
|
|
if <test1>
|
|
event,<EventName1>
|
|
endif
|
|
endon
|
|
|
|
on <EventName1> do
|
|
if <test2>
|
|
<action>
|
|
endif
|
|
endon
|
|
|
|
As of mega-201803.. we have the possibility to use AND/OR:
|
|
|
|
.. code-block:: html
|
|
|
|
on test do
|
|
if [test#a]=0 or [test#b]=0 or [test#c]=0
|
|
event,ok
|
|
else
|
|
event,not_ok
|
|
endif
|
|
endon
|
|
|
|
on test2 do
|
|
if [test#a]=1 and [test#b]=1 and [test#c]=1
|
|
event,ok
|
|
else
|
|
event,not_ok
|
|
endif
|
|
endon
|
|
|
|
on test3 do
|
|
if [test#a]=1 and [test#b]=1 or [test#c]=0
|
|
event,ok
|
|
else
|
|
event,not_ok
|
|
endif
|
|
endon
|
|
|
|
on test4 do
|
|
if [test#a]=0
|
|
event,ok
|
|
else
|
|
event,not_ok
|
|
endif
|
|
endon
|
|
|
|
Up to two AND/OR can be used per if statement, that means that you can test
|
|
three float values and if the statement is true/false corresponding action will take place.
|
|
|
|
Trigger
|
|
-------
|
|
|
|
.. code-block:: html
|
|
|
|
<trigger>
|
|
|
|
The trigger can be an device value being changed:
|
|
|
|
.. code-block:: html
|
|
|
|
DeviceName#ValueName
|
|
|
|
Operator (inequality function)
|
|
------------------------------
|
|
|
|
Or a inequality function:
|
|
|
|
.. code-block:: html
|
|
|
|
DeviceName#ValueName<inequality function><value>
|
|
|
|
Where the "inequality function" is a simple check:
|
|
|
|
.. code-block:: html
|
|
|
|
equal (=) to
|
|
less (<) than
|
|
greater (>) than
|
|
less or equal (<=) to
|
|
greater or equal (>=) to
|
|
not equal (!= or <>) to
|
|
|
|
DeviceName#ValueName<<value>
|
|
DeviceName#ValueName=<value>
|
|
DeviceName#ValueName><value>
|
|
DeviceName#ValueName>=<value>
|
|
DeviceName#ValueName<=<value>
|
|
DeviceName#ValueName!=<value>
|
|
DeviceName#ValueName<><value>
|
|
|
|
(System) events
|
|
---------------
|
|
|
|
Some special cases are these system triggers which is triggered upon
|
|
boot/reboot/time/sleep etc. of the unit:
|
|
|
|
.. include:: ../Plugin/P000_events.repl
|
|
|
|
Test
|
|
----
|
|
|
|
.. code-block:: html
|
|
|
|
<test>
|
|
|
|
As described in the trigger section the test is a check done by checking
|
|
if the DeviceName#ValueName is meeting a criteria:
|
|
|
|
.. code-block:: html
|
|
|
|
[DeviceName#ValueName]<inequality function><value>
|
|
|
|
Where the value must be a float value with a dot as decimal sign. The
|
|
DeviceName#ValueName is closed by (square) brackets "[" and "]".
|
|
|
|
Action
|
|
------
|
|
|
|
.. code-block:: html
|
|
|
|
<action>
|
|
|
|
The action can be any system command found in the [ADD_LINK].
|
|
Also plugin specific command are available as long as the plugin is in use.
|
|
In the case mentioned earlier we use a action to trigger multiple logics
|
|
tests (the "event" command).
|
|
|
|
Comment
|
|
-------
|
|
|
|
If you want you can add comments to any row in your rules code. Just
|
|
remember to add them after the code and always begin with "//":
|
|
|
|
.. code-block:: html
|
|
|
|
on <trigger> do //If this happens then do that...
|
|
if <test>
|
|
<action>
|
|
<action>
|
|
else
|
|
<action>
|
|
endif //this is another comment
|
|
endon
|
|
|
|
Referring values
|
|
----------------
|
|
|
|
Rules and some plugins can use references to other (dynamic) values within ESPeasy.
|
|
|
|
The syntax for referring other values is: ``[...#...]``
|
|
Sometimes it can be useful to have some extra options, each separated using a '#' like this: ``[...#...#...]``
|
|
|
|
Reference to a value of a specific task: ``[TaskName#ValueName]``
|
|
|
|
Referring a value using some pre-defined format: ``[TaskName#ValueName#transformation#justification]``
|
|
|
|
|
|
For example, there is a task named "bme280" which has a value named "temperature".
|
|
|
|
Its value can be referenced like this: ``[bme280#temperature]``.
|
|
This can be used in some plugins like the "OLED Framed" plugin to populate some lines on the display.
|
|
It can also be used in rules. Every occurance of this text will then be replaced by its value.
|
|
|
|
|
|
N.B. these references to task values only yield a value when the task is enabled and its value is valid.
|
|
|
|
|
|
Special task names
|
|
------------------
|
|
|
|
You must not use the task names ``Plugin``, ``var`` ``int`` as these have special meaning.
|
|
|
|
``Plugin`` can be used in a so called ``PLUGIN_REQUEST``, for example:
|
|
``[Plugin#GPIO#Pinstate#N]`` to get the pin state of a GPIO pin.
|
|
|
|
``Var`` and ``int`` are used for internal variables.
|
|
The variables set with the ``Let`` command will be available in rules
|
|
as ``var#N`` or ``int#N`` where ``N`` is 1..16.
|
|
For example: ``Let,10,[var#9]``
|
|
|
|
N.B. ``int`` and ``var`` use the same variable, only ``int`` does round them to 0 decimals.
|
|
N.B.2 ``int`` is added in build 20190916.
|
|
|
|
``Clock``, ``Rules`` and ``System`` etc. are not recommended either since they are used in
|
|
event names.
|
|
|
|
Please observe that task names are case insensitive meaning that VAR, var, and Var etc.
|
|
are all treated the same.
|
|
|
|
|
|
Parameter parsing
|
|
-----------------
|
|
|
|
A command used in rules can have several parameters.
|
|
A parameter is typically separated by a comma, but for convenience the separator can also
|
|
be accompanied by a space or be a single space.
|
|
|
|
So these can be used to separate a parameter:
|
|
|
|
* comma
|
|
* comma space
|
|
* space comma
|
|
* space
|
|
* space space
|
|
|
|
This does impose some restrictions on the use of a comma or a space in a parameter.
|
|
Especially sending JSON to a MQTT controller can become next to impossible with these limitations.
|
|
|
|
In order to allow the comma or space in a parameter, you can wrap the parameter in quotes.
|
|
|
|
* Single quote (')
|
|
* Double quote (")
|
|
* Back quote (added to builds after 2019/11/10)
|
|
|
|
There are multiple quotes available for this, to be able to use "the other quote" in your parameter.
|
|
For example in JSON, you need the double quote for string like values or keys.
|
|
|
|
.. code-block:: html
|
|
|
|
Publish domoticz/in,{"idx":26,"nvalue":0,"svalue":"[AQ#TVOC]"}
|
|
|
|
This can be fixed by wrapping the last parameter with single quotes like this:
|
|
|
|
.. code-block:: html
|
|
|
|
Publish domoticz/in,'{"idx":26,"nvalue":0,"svalue":"[AQ#TVOC]"}'
|
|
|
|
Just make sure to use the same quote as start and end of your parameter and don't use that character in the parameter itself.
|
|
N.B. these extra quotes are removed from the parameter when used, as well as trailing or leading spaces.
|
|
|
|
The reason this behavior was changed from before 2019/11 was that the old implementation could lead to unpredictable results.
|
|
|
|
|
|
Formatting refered values
|
|
-------------------------
|
|
|
|
When referring another value, some basic formatting can be used.
|
|
|
|
Referring a value using some pre-defined format: ``[TaskName#ValueName#transformation#justification]``
|
|
|
|
Transformation
|
|
^^^^^^^^^^^^^^
|
|
|
|
* Transformations are case sensitive. (``M`` differs from ``m``, capital is more verbose)
|
|
* Transformations can not be used on "Plugin" calls, like ``[Plugin#GPIO#Pinstate#N]``, since these already use multiple occurences of ``#``.
|
|
* Most transformations work on "binary" values (logic values 0 or 1)
|
|
* A "binary" transformation can be "inverted" by adding a leading ``!``.
|
|
* A "binary" value is considered 0 when its string value is "0" or empty, otherwise it is an 1. (float values are rounded)
|
|
* A "binary" value can also be used to detect presence of a string, as it is 0 on an empty string or 1 otherwise.
|
|
|
|
Binary transformations:
|
|
|
|
* ``C``: 0 => "CLOSE" 1 => " OPEN"
|
|
* ``H``: 0 => "COLD" 1 => " HOT"
|
|
* ``I``: 0 => "OUT" 1 => " IN"
|
|
* ``M``: 0 => "AUTO" 1 => " MAN"
|
|
* ``m``: 0 => "A" 1 => "M"
|
|
* ``O``: 0 => "OFF" 1 => " ON"
|
|
* ``U``: 0 => "DOWN" 1 => " UP"
|
|
* ``u``: 0 => "D" 1 => "U"
|
|
* ``V``: value = value without transformations
|
|
* ``X``: 0 => "O" 1 => "X"
|
|
* ``Y``: 0 => " NO" 1 => "YES"
|
|
* ``y``: 0 => "N" 1 => "Y"
|
|
* ``Z``: 0 => "0" 1 => "1"
|
|
|
|
Floating point transformations:
|
|
|
|
* ``Dx.y``: Minimal 'x' digits zero filled & 'y' decimal fixed digits. E.g. ``[bme#T#D2.1]``
|
|
* ``Dx``: Minimal 'x' digits zero filled in front of the decimal point, no decimal digits. Same as ``Dx.0``
|
|
* ``D.y``: Same as ``D0.y``
|
|
* ``d``: Same as ``D`` but spaces insted zeros
|
|
* ``F``: Floor (round down)
|
|
* ``E``: cEiling (round up)
|
|
|
|
Justification
|
|
^^^^^^^^^^^^^
|
|
|
|
* ``Pn``: Prefix Fill with n spaces.
|
|
* ``Sn``: Suffix Fill with n spaces.
|
|
* ``Ln``: Left part of the string, n characters.
|
|
* ``Rn``: Right part of the string, n characters.
|
|
* ``Ux.y``: Substring Ux.y where x=firstChar and y=number of characters.
|
|
|
|
|
|
String Formatting and Interpreting
|
|
----------------------------------
|
|
|
|
(added 2020/02/24)
|
|
|
|
String operator commands described here can be recognized by their wrapping curly braces.
|
|
|
|
This helps recognize task values (``[taskname#varname]``) in these commands.
|
|
|
|
|
|
Substring
|
|
^^^^^^^^^
|
|
|
|
It is possible to process sub strings, for example when working with ``%eventvalue%`` in rules.
|
|
|
|
Usage: ``{substring:<startpos>:<endpos>:<string>}``
|
|
|
|
The position arguments are the same as in Arduino ``String::substring`` , meaning the endpos is 1 position further than the last character you need.
|
|
|
|
For example:
|
|
|
|
.. code-block:: html
|
|
|
|
on DS-1#Temperature do
|
|
logentry,{substring:0:1:%eventvalue%}
|
|
logentry,{substring:1:2:%eventvalue%}
|
|
logentry,{substring:2:3:%eventvalue%}
|
|
endon
|
|
|
|
The ``%eventvalue%`` may contain the value "23.06"
|
|
The output in the log will then be:
|
|
|
|
.. code-block:: html
|
|
|
|
1512372 : Info : EVENT: DS-1#Temperature=23.06
|
|
1512404 : Info : ACT : logentry,2
|
|
1512405 : Info : Command: logentry
|
|
1512406 : Info : 2
|
|
1512409 : Info : ACT : logentry,3
|
|
1512410 : Info : Command: logentry
|
|
1512410 : Info : 3
|
|
1512413 : Info : ACT : logentry,.
|
|
1512414 : Info : Command: logentry
|
|
1512415 : Info : .
|
|
|
|
|
|
N.B. it is also possible to concatenate these and refer to ``{taskname#varname}``.
|
|
|
|
For example (bit useless example, just for illustrative purposes):
|
|
|
|
.. code-block:: html
|
|
|
|
on DS-1#Temperature do
|
|
logentry,{substring:0:2:{strtol:16:{substring:0:2:[DS-1#Temperature]}{substring:3:5:[DS-1#Temperature]}}}
|
|
endon
|
|
|
|
.. code-block:: html
|
|
|
|
221313 : Info : EVENT: DS-1#Temperature=22.13
|
|
221346 : Info : parse_string_commands cmd: substring:0:2:22.13 -> 22
|
|
221347 : Info : parse_string_commands cmd: substring:3:5:22.13 -> 13
|
|
221348 : Info : parse_string_commands cmd: strtol:16:2213 -> 8723
|
|
221349 : Info : parse_string_commands cmd: substring:0:2:8723 -> 87
|
|
221350 : Info : ACT : logentry,87
|
|
221351 : Info : Command: logentry
|
|
221353 : Info : 87
|
|
|
|
strtol
|
|
^^^^^^
|
|
|
|
Strings or substrings can be converted from just about any base value (binary, octal, hexadecimal) into an integer value.
|
|
|
|
Usage: ``{strtol:16:<string>}`` to convert HEX (base 16) into an integer value.
|
|
|
|
|
|
Example of extracting sub strings from a value and interpreting as if they were HEX values:
|
|
|
|
.. code-block:: html
|
|
|
|
on DS-1#Temperature do
|
|
logentry,{strtol:16:%eventvalue%}
|
|
logentry,{strtol:16:{substring:3:5:%eventvalue%}}
|
|
endon
|
|
|
|
.. code-block:: html
|
|
|
|
1987550 : Info : EVENT: DS-1#Temperature=24.12
|
|
1987586 : Info : ACT : logentry,36
|
|
1987587 : Info : Command: logentry
|
|
1987588 : Info : 36
|
|
1987591 : Info : ACT : logentry,18
|
|
1987592 : Info : Command: logentry
|
|
1987593 : Info : 18
|
|
|
|
What we see here is the interpretation of "24.12":
|
|
|
|
* 0x24 = 36
|
|
* 0x12 = 18
|
|
|
|
Example use case:
|
|
|
|
|
|
As a use case, imagine the output of ser2net (P020) from an OpenTherm gateway.
|
|
|
|
* Message coming from the serial interface: **T101813C0**
|
|
* The B denotes that the message is from the
|
|
* The next 4 bytes (actually 2bytes hex encoded) denote the status and type of the message.
|
|
* the last 4 bytes (actually 2bytes hex encoded) denote the payload.
|
|
* Message that ends up in rules when using ser2net (P020) and Generic handling: ``!Serial#BT101813C0``
|
|
|
|
The room temperature in this sample is 19.75 C
|
|
|
|
Get the last four bytes in packs of two bytes:
|
|
|
|
* ``{substring:13:15:%eventvalue%}``
|
|
* ``{substring:15:17:%eventvalue%}``
|
|
|
|
Parsing them to decimal representation each (using a base 16 call to strtol):
|
|
|
|
* ``{strtol:16:{substring:13:15:%eventvalue%}}``
|
|
* ``{strtol:16:{substring:15:17:%eventvalue%}}``
|
|
|
|
Last but not least the fraction is not correct, it needs to be divided by 256 (and multiplied by 100)
|
|
|
|
* ``{strtol:16:{substring:15:17:%eventvalue%}}*100/255``
|
|
|
|
Complete rule used to parse this and set a variable in a dummy device:
|
|
|
|
.. code-block:: html
|
|
|
|
// Room temperature
|
|
on !Serial#T1018* do
|
|
TaskValueSet 2,1,{strtol:16:{substring:13:15:%eventvalue%}}.{strtol:16:{substring:15:17:%eventvalue%}}*100/255
|
|
endon
|
|
|
|
|
|
ord
|
|
^^^
|
|
|
|
Give the ordinal/integer value of the first character of a string. (e.g. ASCII integer value)
|
|
|
|
Usage: ``{ord:<string>}``
|
|
|
|
For example:
|
|
|
|
.. code-block:: html
|
|
|
|
on DS-1#Temperature do
|
|
logentry,{ord:A} // ASCII value of 'A'
|
|
logentry,{ord:{substring:2:3:%eventvalue%}} // ASCII value of 3rd character of %eventvalue%
|
|
endon
|
|
|
|
|
|
.. code-block:: html
|
|
|
|
2982455 : Info : EVENT: DS-1#Temperature=23.12
|
|
2982487 : Info : ACT : logentry,65
|
|
2982488 : Info : Command: logentry
|
|
2982489 : Info : 65
|
|
2982492 : Info : ACT : logentry,46
|
|
2982493 : Info : Command: logentry
|
|
2982494 : Info : 46
|
|
|
|
|
|
|
|
System variables
|
|
----------------
|
|
|
|
There is a large number of system variables.
|
|
These do not refer to task values, but to typical system variables like system uptime, current time and date, etc.
|
|
|
|
These can all be seen on the ``<ip-address>/sysvars`` page.
|
|
|
|
N.B. These values cannot be formatted like the task value references.
|
|
|
|
|
|
Best practice
|
|
-------------
|
|
|
|
It is possible to use CAPITAL letters and lower case as you please but best
|
|
practice is to use the same types of letters that are found in the
|
|
[ADD_LINK], and plugin specific commands. For the logics (on, if, else ... )
|
|
the general idea is to use lower case.
|
|
|
|
Regarding spaces in names it is recommended to NOT use them as it makes bug
|
|
testing rules a lot harder. Spaces between chunks of code is possible to make
|
|
the code more readable:
|
|
|
|
.. code-block:: html
|
|
|
|
[DeviceName#ValueName]<<value> //These work...
|
|
[DeviceName#ValueName] < <value> //the same...
|
|
|
|
|
|
Sometimes there is limited space to use a reference, like in some plugins
|
|
or when the maximum size of a rule file has been reached.
|
|
|
|
In such cases it is adviced to use short names for tasks and values.
|
|
For example: ``[bme#T]`` instead of ``[bme280#temperature]``
|
|
|
|
Some working examples
|
|
=====================
|
|
|
|
TaskValueSet
|
|
------------
|
|
|
|
Dummy Device is a single way to store and read value on variable.
|
|
Just create Generic - Dummy Device and variables inside it.
|
|
|
|
.. code-block:: html
|
|
|
|
TaskValueSet,TASKnr,VARnr,Value
|
|
|
|
Alternatively, TASKname and/or VARname can be used instead of TASKnr and VARnr:
|
|
|
|
.. code-block:: html
|
|
|
|
TaskValueSet,TASKname,VARname,Value
|
|
TaskValueSet,TASKnr,VARname,Value
|
|
TaskValueSet,TASKname,VARnr,Value
|
|
|
|
This example for two switches that toggle one device (LED and Relay on GPIO 13 and 16).
|
|
|
|
|
|
.. code-block:: html
|
|
|
|
on sw1#state do
|
|
if [dummy#var1]=0
|
|
TaskValueSet 12,1,0
|
|
else
|
|
TaskValueSet 12,1,1
|
|
endif
|
|
gpio,16,[dummy#var1]
|
|
gpio,13,[dummy#var1]
|
|
endon
|
|
|
|
on sw1a#state do
|
|
if [dummy#var1]=0
|
|
TaskValueSet 12,1,1
|
|
else
|
|
TaskValueSet 12,1,0
|
|
endif
|
|
gpio,16,[dummy#var1]
|
|
gpio,13,[dummy#var1]
|
|
endon
|
|
|
|
// Alternative for above example using TASKname/VARname
|
|
on sw1#state do
|
|
if [dummy#var1]=0
|
|
TaskValueSet dummy,var1,0
|
|
else
|
|
TaskValueSet dummy,var1,1
|
|
endif
|
|
gpio,16,[dummy#var1]
|
|
gpio,13,[dummy#var1]
|
|
endon
|
|
|
|
on sw1a#state do
|
|
if [dummy#var1]=0
|
|
TaskValueSet dummy,var1,1
|
|
else
|
|
TaskValueSet dummy,var1,0
|
|
endif
|
|
gpio,16,[dummy#var1]
|
|
gpio,13,[dummy#var1]
|
|
endon
|
|
|
|
Please note that the values stored in a Dummy Value are of type float.
|
|
This does mean you only have about 20 bits of resolution for the value.
|
|
|
|
Storing large numbers like the unix time (31 bits of resolution needed) do need some tricks to be stored.
|
|
For the Unix time there are now 2 variables included:
|
|
|
|
- %unixday%
|
|
- %unixday_sec%
|
|
|
|
Here some example used to store the Unix time in the dummy plugin to keep track of actions.
|
|
The values stored in the Dummy variables will be kept and restored on a crash/reboot as long as the ESP remains powered.
|
|
|
|
.. code-block:: html
|
|
|
|
if [DT#YMD]=0 and %unixday%>0
|
|
taskvalueset,7,1,%unixday%-1
|
|
endif
|
|
if %unixday%>0
|
|
let,5,%unixday%-[DT#YMD]
|
|
let,4,%v5%*86400-[DT#HMS]+%unixday_sec%
|
|
else
|
|
let,4,0
|
|
endif
|
|
if %v4%>[Config#MinWateringDelay]
|
|
event,Irrigate
|
|
endif
|
|
|
|
|
|
|
|
Event value (%eventvalue%)
|
|
--------------------------
|
|
|
|
Rules engine specific:
|
|
|
|
%eventvalue% - substitutes the event value (everything that comes after
|
|
the '=' sign, up to four values are possible).
|
|
|
|
Sample rules section:
|
|
|
|
.. code-block:: html
|
|
|
|
on remoteTimerControl do
|
|
timerSet,1,%eventvalue%
|
|
endon
|
|
|
|
Now send this command to the ESP:
|
|
|
|
.. code-block:: html
|
|
|
|
http://<espeasyip>/control?cmd=event,remoteTimerControl=5
|
|
|
|
and it will set rules timer no 1 to 5 seconds. Using this technique you can
|
|
parse a value from an event to the rule engine.
|
|
|
|
.. note::
|
|
'timerSet' is a rule command and cannot be run directly from a remote command.
|
|
|
|
If you want to check the transferred value within rules on the receiving ESP
|
|
(condition in if-statement), you will need to write the transferred value into
|
|
a Dummy device using the TaskValueSet command. It is then possible to check
|
|
the value of the Dummy device as condition in if-statement within rules.
|
|
|
|
Multiple event values:
|
|
|
|
.. code-block:: html
|
|
|
|
on ToggleGPIO do
|
|
GPIO,%eventvalue1%,%eventvalue2%
|
|
endon
|
|
|
|
You could then use the command "ToggleGPIO" with dynamic GPIO numbers and state.
|
|
|
|
.. code-block:: html
|
|
|
|
http://<espeasyip>/control?cmd=event,ToggleGPIO=12,1
|
|
|
|
Internal variables
|
|
------------------
|
|
|
|
A really great feature to use is the 16 internal variables. You set them like this:
|
|
|
|
.. code-block:: html
|
|
|
|
Let,<n>,<value>
|
|
|
|
Where n can be 1 to 16 and the value an float. To use the values in strings you can
|
|
either use the ``%v7%`` syntax or ``[var#7]``. BUT for formulas you need to use the square
|
|
brackets in order for it to compute, i.e. ``[var#12]``.
|
|
|
|
|
|
Averaging filters
|
|
-----------------
|
|
|
|
You may want to clear peaks in otherwise jumpy measurements and if you cannot
|
|
remove the jumpiness with hardware you might want to add a filter in the software.
|
|
|
|
A **10 value average**:
|
|
|
|
.. code-block:: html
|
|
|
|
On Temp#Value Do
|
|
Let,10,[VAR#9]
|
|
Let,9,[VAR#8]
|
|
Let,8,[VAR#7]
|
|
Let,7,[VAR#6]
|
|
Let,6,[VAR#5]
|
|
Let,5,[VAR#4]
|
|
Let,4,[VAR#3]
|
|
Let,3,[VAR#2]
|
|
Let,2,[VAR#1]
|
|
Let,1,[Temp#Value]
|
|
TaskValueSet,12,1,([VAR#1]+[VAR#2]+[VAR#3]+[VAR#4]+[VAR#5]+[VAR#6]+[VAR#7]+[VAR#8]+[VAR#9]+[VAR#10])/10
|
|
EndOn
|
|
|
|
In the above example we use the sensor value of ``Temp#Value`` to get the trigger event,
|
|
we then add all the previous 9 values to the internal variables and the newly acquired
|
|
value to the first variable. We then summarize them and divide them by 10 and store it
|
|
as a dummy variable (example is on task 12, value 1) which we use to publish the sliding
|
|
value instead of the sensor value.
|
|
|
|
Another filter could be to just use the previous value and **dilute** the new value with that one:
|
|
|
|
.. code-block:: html
|
|
|
|
On Temp#Value Do
|
|
Let,2,[VAR#1]
|
|
Let,1,[Temp#Value]
|
|
TaskValueSet,12,1,(3*[VAR#1]+[VAR#2])/4
|
|
EndOn
|
|
|
|
|
|
Yet another filter could be to add the new value to a **summarized average**:
|
|
|
|
.. code-block:: html
|
|
|
|
On Temp#Value Do
|
|
Let,1,[Temp#Value]
|
|
TaskValueSet,12,1,([VAR#1]+3*[VAR#2])/4
|
|
Let,2,[Dummy#Value]
|
|
EndOn
|
|
|
|
What you should use? That is a case by case question. Try them all and see which
|
|
one suits your actual scenario the best.
|
|
|
|
PIR and LDR
|
|
-----------
|
|
|
|
.. code-block:: html
|
|
|
|
On PIR#State do
|
|
if [LDR#Light]<500
|
|
gpio,16,[PIR#State]
|
|
endif
|
|
endon
|
|
|
|
.. note::
|
|
|
|
In other words: If the PIR switch is set (to either 1 or 0) and if
|
|
the light value < 500, then set GPIO port 16 of the ESP.
|
|
|
|
.. code-block:: html
|
|
|
|
on PIR#State=1 do
|
|
if [LDR#Light]<500
|
|
gpio,16,[PIR#State]
|
|
endif
|
|
endon
|
|
|
|
Now the event is only triggered when the PIR switches on.
|
|
|
|
SR04 and LDR
|
|
------------
|
|
|
|
.. code-block:: html
|
|
|
|
on SR04#range<100 do
|
|
if [ldr#lux]<500
|
|
gpio,2,0
|
|
gpio,16,1
|
|
else
|
|
gpio,2,1
|
|
gpio,16,0
|
|
endif
|
|
endon
|
|
|
|
|
|
Timer
|
|
-----
|
|
|
|
There are 8 timers (1-8) you can use:
|
|
|
|
.. code-block:: html
|
|
|
|
On System#Boot do //When the ESP boots, do
|
|
servo,1,12,0
|
|
timerSet,1,10 //Set Timer 1 for the next event in 10 seconds
|
|
endon
|
|
|
|
On Rules#Timer=1 do //When Timer1 expires, do
|
|
servo,1,12,30
|
|
timerSet,2,1 //Set Timer 2 for the next event in 1 second
|
|
endon
|
|
|
|
On Rules#Timer=2 do //When Timer2 expires, do
|
|
servo,1,12,0
|
|
timerSet,1,30 //Set Timer1 for the next event in 30 seconds
|
|
endon
|
|
|
|
|
|
Starting/stopping repeating timers with events
|
|
----------------------------------------------
|
|
|
|
To disable an existing timer, set it to 0. This is useful to make repeating
|
|
timers for things like alarms or warnings:
|
|
|
|
.. code-block:: html
|
|
|
|
//start the warning signal when we receive a start_warning event:
|
|
On start_warning do
|
|
timerSet,1,2
|
|
endon
|
|
|
|
//stop the warning signal when we receive a stop_warning event:
|
|
On stop_warning do
|
|
timerSet,1,0
|
|
endon
|
|
|
|
//create an actual warning signal, every time timer 1 expires:
|
|
On Rules#Timer=1 do
|
|
//repeat after 2 seconds
|
|
timerSet,1,2
|
|
//pulse some led on pin 4 shortly
|
|
Pulse,4,1,100
|
|
//produce a short 1000hz beep via a piezo element on pin 14
|
|
tone,14,1000,100
|
|
endon
|
|
|
|
To start or stop the warning signal use http:
|
|
|
|
.. code-block:: html
|
|
|
|
http://<espeasyip>/control?cmd=event,start_warning
|
|
http://<espeasyip>/control?cmd=event,stop_warning
|
|
|
|
HTTP call
|
|
---------
|
|
|
|
When you enter this first command with the correct IP address in the URL of your browser:
|
|
|
|
.. code-block:: html
|
|
|
|
http://<espeasyip>/control?cmd=event,startwatering
|
|
http://<espeasyip>/control?cmd=event,stopwatering
|
|
|
|
And have this rule in the addressed ESP:
|
|
|
|
.. code-block:: html
|
|
|
|
On startwatering do
|
|
gpio,12,1 //start watering (open valve)
|
|
timerSet,1,600 //timer 1 set for 10 minutes
|
|
endon
|
|
|
|
On stopwatering do
|
|
timerSet,1,0 //timer 1 set to halt, used to stop watering before the timer ends!
|
|
gpio,12,0 //stop watering (close valve)
|
|
endon
|
|
|
|
On Rules#Timer=1 do
|
|
gpio,12,0 //stop watering (close valve)
|
|
endOn
|
|
|
|
|
|
Provided that you also have the valve etc., the plants will be happy.
|
|
|
|
SendTo and Publish
|
|
------------------
|
|
|
|
With SendTo you can add a Rule to your ESP Easy, capable of sending an event to another unit.
|
|
This can be useful in cases where you want to take immediate action.
|
|
There are two flavors:
|
|
- SendTo to send remote unit control commands using the internal peer to peer UDP messaging
|
|
- Publish to send remote commands to other ESP using MQTT broker
|
|
|
|
SendTo: SendTo <unit>,<command>
|
|
|
|
|
|
Imagine you have two ESP Easy modules, ESP#1 and ESP#2
|
|
In the Rules section of ESP#1 you have this:
|
|
|
|
.. code-block:: html
|
|
|
|
on demoEvent do
|
|
sendTo,2,event,startwatering //(to use the previous example.)
|
|
endon
|
|
|
|
And ESP#2 has the rules according to the previous example (givemesomewater)
|
|
|
|
If you then enter this with the correct IP address in the URL of your browser:
|
|
|
|
.. code-block:: html
|
|
|
|
http://<ESP#1-ip >/control?cmd=event,demoEvent
|
|
|
|
Then ESP#1 shall send the event 'startwatering ' to ESP#2.
|
|
|
|
It is also possible to directly order GPIO changes, like:
|
|
|
|
.. code-block:: html
|
|
|
|
on demoEvent do
|
|
sendTo,2,GPIO,2,1
|
|
endon
|
|
|
|
|
|
Publish
|
|
|
|
.. code-block:: html
|
|
|
|
Publish,<topic>,<value>
|
|
|
|
To be created.
|
|
|
|
Time
|
|
----
|
|
|
|
With Rules you can also start or stop actions on a given day and time, or even on every day.
|
|
|
|
.. code-block:: html
|
|
|
|
On Clock#Time=All,18:25 do // every day at 18:25 hours do ...
|
|
gpio,14,0
|
|
endon
|
|
|
|
Or for a specific day:
|
|
|
|
.. code-block:: html
|
|
|
|
On Clock#Time=Sun,18:25 do // for Sunday, but All, Sun, Mon, Tue, Wed, Thu, Fri, Sat will do.
|
|
gpio,14,0
|
|
endon
|
|
|
|
It is also possible to use the system value %systime% in rules conditions
|
|
to make things happen during certain hours of the day:
|
|
|
|
.. code-block:: html
|
|
|
|
On Pir#State=1 do
|
|
If %systime% < 07:00:00
|
|
Gpio,16,0
|
|
Endif
|
|
If %systime% > 19:00:00
|
|
Gpio,16,1
|
|
Endif
|
|
Endon
|
|
|
|
This will set GPIO 16 to 1 when the PIR is triggered, if the time is
|
|
before 7 in the morning or after 19:00 in the evening
|
|
( useful if you don't have a light sensor).
|
|
|
|
SendToHTTP
|
|
----------
|
|
|
|
To send a message to another device, like a command to switch on a light to Domoticz
|
|
|
|
.. code-block:: html
|
|
|
|
On System#Boot do //When the ESP boots, do
|
|
timerSet,1,10 //Set Timer 1 for the next event in 10 seconds
|
|
endon
|
|
|
|
On Rules#Timer=1 do //When Timer1 expires, do
|
|
SendToHTTP 192.168.0.243,8080,/json.htm?type=command¶m=switchlight&idx=174&switchcmd=On
|
|
endon
|
|
|
|
Many users have reported problems with commands being truncated, particularly
|
|
when trying to send commands to Domoticz. It seems to be a parsing error.
|
|
There is the following workaround:
|
|
|
|
.. code-block:: html
|
|
|
|
SendToHTTP 192.168.0.243,8080,/json.htm?type=param=switchlight&command&idx=174&switchcmd=On
|
|
|
|
|
|
Dew Point for temp/humidity sensors (BME280 for example)
|
|
--------------------------------------------------------
|
|
|
|
If you have a sensor that is monitoring the air temperature and the relative
|
|
humidity you may calculate the dew point with rules. This example use MQTT to
|
|
publish the values but you may change this to whatever you want. We also make
|
|
use of a 'dummy device' to dump values, this example use two BME280 with
|
|
different i2c addresses.
|
|
|
|
For dew point on the 'outside':
|
|
|
|
.. code-block:: html
|
|
|
|
on TempHumidityPressure_OUTSIDE#%RH do
|
|
TaskValueSet,7,1,[TempHumidityPressure_OUTSIDE#°C]-(100-[TempHumidityPressure_OUTSIDE#%RH])/5 // "7" is the number of the task that the dummy device is on, "1" is its first value where we dump our result
|
|
if [TempHumidityPressure_OUTSIDE#%RH]>49
|
|
Publish,%sysname%/DewPoint_OUTSIDE/°C,[Dew_point#°C1]
|
|
else
|
|
Publish,%sysname%/DewPoint_OUTSIDE/°C,[Dew_point#°C1]* //This asterix shows that the calculation is not correct due to the humidity being below 50%!
|
|
endif
|
|
endon
|
|
|
|
For dew point on the 'inside':
|
|
|
|
.. code-block:: html
|
|
|
|
on TempHumidityPressure_INSIDE#%RH do
|
|
TaskValueSet,7,2,[TempHumidityPressure_INSIDE#°C]-(100-[TempHumidityPressure_INSIDE#%RH])/5 // "7" is the number of the task that the dummy device is on, "2" is its second value where we dump our result
|
|
if [TempHumidityPressure_INSIDE#%RH]>49
|
|
Publish,%sysname%/DewPoint_INSIDE/°C,[Dew_point#°C2]
|
|
else
|
|
Publish,%sysname%/DewPoint_INSIDE/°C,[Dew_point#°C2]* //This asterix shows that the calculation is not correct due to the humidity being below 50%!
|
|
endif
|
|
endon
|
|
|
|
|
|
Report IP every 30 seconds using MQTT
|
|
-------------------------------------
|
|
|
|
This rule also work as a ping or heart beat of the unit. If it has not
|
|
published a IP number for 30+ seconds the unit is experiencing problems.
|
|
|
|
.. code-block:: html
|
|
|
|
On System#Boot do //When the ESP boots, do
|
|
Publish,%sysname%/IP,%ip%
|
|
timerSet,1,30 //Set Timer 1 for the next event in 30 seconds
|
|
endon
|
|
|
|
On Rules#Timer=1 do //When Timer1 expires, do
|
|
Publish,%sysname%/IP,%ip%
|
|
timerSet,1,30 //Resets the Timer 1 for another 30 seconds
|
|
endon
|
|
|
|
Custom reports to Domoticz with own IDX
|
|
---------------------------------------
|
|
|
|
This rule was presented as a workaround for a problem where a sensor had
|
|
three different values but only one IDX value. You could publish your own
|
|
Domoticz messages (MQTT or HTTP) using this method. Below we use the INA219
|
|
plugin that have 3 values which of the two second ones are Amps and Watts,
|
|
just as an example we want to publish these as custom messages with a unique IDX value.
|
|
|
|
*MQTT*
|
|
|
|
.. code-block:: html
|
|
|
|
on INA219#Amps do
|
|
Publish domoticz/in,{"idx":123456,"nvalue":0,"svalue":"[INA219#Amps]"} //Own made up IDX 123456
|
|
endon
|
|
|
|
on INA219#Watts do
|
|
Publish domoticz/in,{"idx":654321,"nvalue":0,"svalue":"[INA219#Watts]"} //Own made up IDX 654321
|
|
endon
|
|
|
|
|
|
*HTTP*
|
|
|
|
.. code-block:: html
|
|
|
|
on INA219#Amps do
|
|
SendToHTTP 192.168.1.2,8080,/json.htm?type=command¶m=udevice&idx=123456&nvalue=0&svalue=[INA219#Amps] //Own made up IDX 123456
|
|
endon
|
|
|
|
on INA219#Watts do
|
|
SendToHTTP 192.168.1.2,8080,/json.htm?type=command¶m=udevice&idx=654321&nvalue=0&svalue=[INA219#Watts] //Own made up IDX 654321
|
|
endon
|
|
|
|
(Given that your Domoticz server is on "192.168.1.2:8080", you should change
|
|
to your server IP and PORT number. If the HTTP publishing is not working,
|
|
please refer to this [ADD_LINK] for a workaround.)
|
|
|
|
One button, multiple actions using long press
|
|
---------------------------------------------
|
|
|
|
Using a "normal switch" device which is in this example normally set to low
|
|
(0) you can make one of two actions when pressed. If you either release the
|
|
button in less than a second or press it for more than a second:
|
|
|
|
.. code-block:: html
|
|
|
|
on Button#State=1 do
|
|
timerSet,1,1
|
|
endon
|
|
|
|
on rules#timer=1 do
|
|
if [Button#State]=0
|
|
//Action if button is short pressed
|
|
else
|
|
//Action if button is still pressed
|
|
endif
|
|
endon
|
|
|
|
Calculating water consumption
|
|
-----------------------------
|
|
|
|
Using the pulse counter you can calculate and act on waterflow and changes like this:
|
|
|
|
.. code-block:: html
|
|
|
|
On System#Boot do // When the ESP boots, do
|
|
TaskValueSet,3,1,0 // TaskValueSet TASKnr,VARnr,Value, Reset the Liters counter to 0
|
|
TaskValueSet,3,2,0 // TaskValueSet TASKnr,VARnr,Value, Reset the PreviousLiters counter to 0
|
|
TaskValueSet,3,3,0 // TaskValueSet TASKnr,VARnr,Value, Reset the Flow counter to 0
|
|
TaskValueSet,3,4,0 // TaskValueSet TASKnr,VARnr,Value, Reset the PreviousFlow counter to 0
|
|
TimerSet,1,30 // Set Timer 1 for the next event in 30 seconds
|
|
EndOn
|
|
|
|
On Watermeter#Count do // When Pulse is detected
|
|
if [Watermeter#Count] > 0
|
|
SendToHTTP,192.168.1.50,8084,/json.htm?type=command¶m=udevice&idx=337&nvalue=0&svalue=1
|
|
TaskValueSet,3,3,60000/[Watermeter#Time]
|
|
SendToHTTP,192.168.1.50,8084,/json.htm?type=command¶m=udevice&idx=338&nvalue=0&svalue=[Liters#Flow]
|
|
endif
|
|
EndOn
|
|
|
|
On Rules#Timer=1 do // When Timer 1 expires, do
|
|
if [Liters#Flow] > 0 or [Liters#PreviousFlow] > 0 // Only send value if amount of Liters > 0
|
|
SendToHTTP,192.168.1.50,8084,/json.htm?type=command¶m=udevice&idx=338&nvalue=0&svalue=[Liters#Flow]
|
|
TaskValueSet,3,4,[Liters#Flow] // set flow to previous counter
|
|
TaskValueSet,3,3,0
|
|
endif
|
|
TimerSet,1,30 // Set Timer 1 for the next event in 30 seconds
|
|
Endon
|