2018-04-13 16:42:11 +01:00
/*
2019-10-27 10:13:24 +00:00
xdrv_10_rules . ino - rule support for Tasmota
2018-04-13 16:42:11 +01:00
2019-01-01 12:55:01 +00:00
Copyright ( C ) 2019 ESP Easy Group and Theo Arends
2018-04-13 16:42:11 +01:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# ifdef USE_RULES
2019-05-18 12:34:52 +01:00
# ifndef USE_SCRIPT
2018-04-13 16:42:11 +01:00
/*********************************************************************************************\
* Rules based heavily on ESP Easy implementation
*
* Inspiration : https : //github.com/letscontrolit/ESPEasy
*
* Add rules using the following , case insensitive , format :
* on < trigger1 > do < commands > endon on < trigger2 > do < commands > endon . .
*
* Examples :
* on System # Boot do Color 001000 endon
* on INA219 # Current > 0.100 do Dimmer 10 endon
* on INA219 # Current > 0.100 do Backlog Dimmer 10 ; Color 10 , 0 , 0 endon
* on INA219 # Current > 0.100 do Backlog Dimmer 10 ; Color 100000 endon on System # Boot do color 001000 endon
* on ds18b20 # temperature > 23 do power off endon on ds18b20 # temperature < 22 do power on endon
* on mqtt # connected do color 000010 endon
* on mqtt # disconnected do color 00100 C endon
* on time # initialized do color 001000 endon
2018-06-26 15:22:53 +01:00
* on time # initialized > 120 do color 001000 endon
2018-04-13 16:42:11 +01:00
* on time # set do color 00100 8 endon
* on clock # timer = 3 do color 080800 endon
* on rules # timer = 1 do color 080800 endon
* on mqtt # connected do color 000010 endon on mqtt # disconnected do color 001010 endon on time # initialized do color 001000 endon on time # set do backlog color 000 810 ; ruletimer1 10 endon on rules # timer = 1 do color 080800 endon
* on event # anyname do color 100000 endon
2018-04-14 13:39:16 +01:00
* on event # anyname do color % value % endon
2018-04-13 16:42:11 +01:00
* on power1 # state = 1 do color 001000 endon
2018-04-14 13:39:16 +01:00
* on button1 # state do publish cmnd / ring2 / power % value % endon on button2 # state do publish cmnd / strip1 / power % value % endon
* on switch1 # state do power2 % value % endon
2018-05-06 15:07:42 +01:00
* on analog # a0div10 do publish cmnd / ring2 / dimmer % value % endon
2018-04-13 16:42:11 +01:00
*
* Notes :
* Spaces after < on > , around < do > and before < endon > are mandatory
* System # Boot is initiated after MQTT is connected due to command handling preparation
* Control rule triggering with command :
* Rule 0 = Rules disabled ( Off )
* Rule 1 = Rules enabled ( On )
* Rule 2 = Toggle rules state
* Rule 4 = Perform commands as long as trigger is met ( Once OFF )
* Rule 5 = Perform commands once until trigger is not met ( Once ON )
* Rule 6 = Toggle Once state
* Execute an event like :
* Event anyname = 001000
* Set a RuleTimer to 100 seconds like :
* RuleTimer2 100
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-11-07 09:30:03 +00:00
# define XDRV_10 10
2018-04-13 16:42:11 +01:00
# define D_CMND_RULE "Rule"
# define D_CMND_RULETIMER "RuleTimer"
# define D_CMND_EVENT "Event"
2018-05-20 16:46:00 +01:00
# define D_CMND_VAR "Var"
# define D_CMND_MEM "Mem"
2018-06-10 06:09:11 +01:00
# define D_CMND_ADD "Add"
# define D_CMND_SUB "Sub"
# define D_CMND_MULT "Mult"
# define D_CMND_SCALE "Scale"
2018-11-27 00:22:44 +00:00
# define D_CMND_CALC_RESOLUTION "CalcRes"
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
# define D_CMND_SUBSCRIBE "Subscribe"
# define D_CMND_UNSUBSCRIBE "Unsubscribe"
2019-09-09 16:24:27 +01:00
# define D_CMND_IF "If"
2018-04-13 16:42:11 +01:00
# define D_JSON_INITIATED "Initiated"
2019-02-05 16:45:35 +00:00
# define COMPARE_OPERATOR_NONE -1
# define COMPARE_OPERATOR_EQUAL 0
# define COMPARE_OPERATOR_BIGGER 1
# define COMPARE_OPERATOR_SMALLER 2
2019-02-05 14:34:17 +00:00
# define COMPARE_OPERATOR_EXACT_DIVISION 3
# define COMPARE_OPERATOR_NUMBER_EQUAL 4
# define COMPARE_OPERATOR_NOT_EQUAL 5
# define COMPARE_OPERATOR_BIGGER_EQUAL 6
# define COMPARE_OPERATOR_SMALLER_EQUAL 7
# define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL
2019-02-05 16:45:35 +00:00
const char kCompareOperators [ ] PROGMEM = " = \0 > \0 < \0 | \0 ==!=>=<= " ;
2019-02-05 14:34:17 +00:00
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# ifdef USE_EXPRESSION
2019-02-16 15:17:17 +00:00
# include <LinkedList.h> // Import LinkedList library
2019-09-28 02:52:33 +01:00
const char kExpressionOperators [ ] PROGMEM = " +-*/%^ \0 " ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# define EXPRESSION_OPERATOR_ADD 0
# define EXPRESSION_OPERATOR_SUBTRACT 1
# define EXPRESSION_OPERATOR_MULTIPLY 2
# define EXPRESSION_OPERATOR_DIVIDEDBY 3
# define EXPRESSION_OPERATOR_MODULO 4
# define EXPRESSION_OPERATOR_POWER 5
const uint8_t kExpressionOperatorsPriorities [ ] PROGMEM = { 1 , 1 , 2 , 2 , 3 , 4 } ;
# define MAX_EXPRESSION_OPERATOR_PRIORITY 4
2019-09-09 16:24:27 +01:00
# define LOGIC_OPERATOR_AND 1
# define LOGIC_OPERATOR_OR 2
# define IF_BLOCK_INVALID -1
# define IF_BLOCK_ANY 0
# define IF_BLOCK_ELSEIF 1
# define IF_BLOCK_ELSE 2
# define IF_BLOCK_ENDIF 3
2019-02-16 15:17:17 +00:00
# endif // USE_EXPRESSION
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
2019-08-11 17:12:18 +01:00
const char kRulesCommands [ ] PROGMEM = " | " // No prefix
2019-08-02 15:23:17 +01:00
D_CMND_RULE " | " D_CMND_RULETIMER " | " D_CMND_EVENT " | " D_CMND_VAR " | " D_CMND_MEM " | "
D_CMND_ADD " | " D_CMND_SUB " | " D_CMND_MULT " | " D_CMND_SCALE " | " D_CMND_CALC_RESOLUTION
# ifdef SUPPORT_MQTT_EVENT
" | " D_CMND_SUBSCRIBE " | " D_CMND_UNSUBSCRIBE
2019-09-09 16:24:27 +01:00
# endif
# ifdef SUPPORT_IF_STATEMENT
" | " D_CMND_IF
2019-08-02 15:23:17 +01:00
# endif
;
void ( * const RulesCommand [ ] ) ( void ) PROGMEM = {
& CmndRule , & CmndRuleTimer , & CmndEvent , & CmndVariable , & CmndMemory ,
& CmndAddition , & CmndSubtract , & CmndMultiply , & CmndScale , & CmndCalcResolution
# ifdef SUPPORT_MQTT_EVENT
, & CmndSubscribe , & CmndUnsubscribe
2019-09-09 16:24:27 +01:00
# endif
# ifdef SUPPORT_IF_STATEMENT
, & CmndIf
2019-08-02 15:23:17 +01:00
# endif
} ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
# ifdef SUPPORT_MQTT_EVENT
# include <LinkedList.h> // Import LinkedList library
typedef struct {
String Event ;
String Topic ;
String Key ;
} MQTT_Subscription ;
LinkedList < MQTT_Subscription > subscriptions ;
2019-08-02 15:23:17 +01:00
# endif // SUPPORT_MQTT_EVENT
2018-04-13 16:42:11 +01:00
2019-08-17 13:27:41 +01:00
struct RULES {
String event_value ;
unsigned long timer [ MAX_RULE_TIMERS ] = { 0 } ;
uint32_t triggers [ MAX_RULE_SETS ] = { 0 } ;
uint8_t trigger_count [ MAX_RULE_SETS ] = { 0 } ;
long new_power = - 1 ;
long old_power = - 1 ;
long old_dimm = - 1 ;
uint16_t last_minute = 60 ;
uint16_t vars_event = 0 ;
uint8_t mems_event = 0 ;
bool teleperiod = false ;
char event_data [ 100 ] ;
} Rules ;
char rules_vars [ MAX_RULE_VARS ] [ 33 ] = { { 0 } } ;
2019-01-15 15:30:20 +00:00
# if (MAX_RULE_VARS>16)
# error MAX_RULE_VARS is bigger than 16
2019-01-04 19:26:37 +00:00
# endif
2019-01-15 15:30:20 +00:00
# if (MAX_RULE_MEMS>5)
# error MAX_RULE_MEMS is bigger than 5
2019-01-04 19:26:37 +00:00
# endif
2018-05-19 07:00:48 +01:00
2018-04-13 16:42:11 +01:00
/*******************************************************************************************/
2019-01-28 13:08:33 +00:00
bool RulesRuleMatch ( uint8_t rule_set , String & event , String & rule )
2018-04-13 16:42:11 +01:00
{
// event = {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}}
// event = {"System":{"Boot":1}}
// rule = "INA219#CURRENT>0.100"
bool match = false ;
2018-05-20 16:46:00 +01:00
char stemp [ 10 ] ;
2018-04-13 16:42:11 +01:00
// Step1: Analyse rule
int pos = rule . indexOf ( ' # ' ) ;
2018-04-17 14:34:18 +01:00
if ( pos = = - 1 ) { return false ; } // No # sign in rule
2018-04-13 16:42:11 +01:00
String rule_task = rule . substring ( 0 , pos ) ; // "INA219" or "SYSTEM"
2019-08-17 13:27:41 +01:00
if ( Rules . teleperiod ) {
2018-04-27 17:06:19 +01:00
int ppos = rule_task . indexOf ( " TELE- " ) ; // "TELE-INA219" or "INA219"
if ( ppos = = - 1 ) { return false ; } // No pre-amble in rule
rule_task = rule . substring ( 5 , pos ) ; // "INA219" or "SYSTEM"
}
2019-09-09 16:24:27 +01:00
String rule_expr = rule . substring ( pos + 1 ) ; // "CURRENT>0.100" or "BOOT" or "%var1%" or "MINUTE|5"
String rule_name , rule_param ;
int8_t compareOperator = parseCompareExpression ( rule_expr , rule_name , rule_param ) ; //Parse the compare expression.Return operator and the left, right part of expression
2018-04-13 16:42:11 +01:00
2019-10-12 15:42:11 +01:00
char rule_svalue [ 80 ] = { 0 } ;
2019-07-01 17:20:43 +01:00
float rule_value = 0 ;
2019-09-09 16:24:27 +01:00
if ( compareOperator ! = COMPARE_OPERATOR_NONE ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_VARS ; i + + ) {
2018-05-20 16:46:00 +01:00
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%VAR%d%% " ) , i + 1 ) ;
if ( rule_param . startsWith ( stemp ) ) {
2019-08-17 13:27:41 +01:00
rule_param = rules_vars [ i ] ;
2018-05-20 16:46:00 +01:00
break ;
}
}
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_MEMS ; i + + ) {
2018-05-20 16:46:00 +01:00
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%MEM%d%% " ) , i + 1 ) ;
if ( rule_param . startsWith ( stemp ) ) {
rule_param = Settings . mems [ i ] ;
break ;
}
}
2018-08-27 08:59:17 +01:00
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%TIME%% " ) ) ;
if ( rule_param . startsWith ( stemp ) ) {
2019-02-19 13:49:15 +00:00
rule_param = String ( MinutesPastMidnight ( ) ) ;
2018-08-27 08:59:17 +01:00
}
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%UPTIME%% " ) ) ;
if ( rule_param . startsWith ( stemp ) ) {
2019-02-19 13:49:15 +00:00
rule_param = String ( MinutesUptime ( ) ) ;
2018-08-27 08:59:17 +01:00
}
2018-12-27 17:57:27 +00:00
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%TIMESTAMP%% " ) ) ;
if ( rule_param . startsWith ( stemp ) ) {
rule_param = GetDateAndTime ( DT_LOCAL ) . c_str ( ) ;
}
2018-08-27 08:59:17 +01:00
# if defined(USE_TIMERS) && defined(USE_SUNRISE)
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%SUNRISE%% " ) ) ;
if ( rule_param . startsWith ( stemp ) ) {
2019-02-19 13:49:15 +00:00
rule_param = String ( SunMinutes ( 0 ) ) ;
2018-08-27 08:59:17 +01:00
}
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%SUNSET%% " ) ) ;
if ( rule_param . startsWith ( stemp ) ) {
2019-02-19 13:49:15 +00:00
rule_param = String ( SunMinutes ( 1 ) ) ;
2018-08-27 08:59:17 +01:00
}
# endif // USE_TIMERS and USE_SUNRISE
2018-05-20 16:46:00 +01:00
rule_param . toUpperCase ( ) ;
2019-03-08 18:24:02 +00:00
strlcpy ( rule_svalue , rule_param . c_str ( ) , sizeof ( rule_svalue ) ) ;
2018-05-20 16:46:00 +01:00
2018-05-24 16:08:14 +01:00
int temp_value = GetStateNumber ( rule_svalue ) ;
2018-05-06 15:07:42 +01:00
if ( temp_value > - 1 ) {
rule_value = temp_value ;
} else {
2019-07-01 17:20:43 +01:00
rule_value = CharToFloat ( ( char * ) rule_svalue ) ; // 0.1 - This saves 9k code over toFLoat()!
2018-05-06 15:07:42 +01:00
}
2018-04-13 16:42:11 +01:00
}
// Step2: Search rule_task and rule_name
2018-05-01 10:28:36 +01:00
StaticJsonBuffer < 1024 > jsonBuf ;
2018-04-13 16:42:11 +01:00
JsonObject & root = jsonBuf . parseObject ( event ) ;
2018-04-17 14:34:18 +01:00
if ( ! root . success ( ) ) { return false ; } // No valid JSON data
2018-04-13 16:42:11 +01:00
2019-09-22 15:14:34 +01:00
const char * str_value ;
if ( ( pos = rule_name . indexOf ( " [ " ) ) > 0 ) { // "CURRENT[1]"
2019-09-22 15:33:14 +01:00
int rule_name_idx = rule_name . substring ( pos + 1 ) . toInt ( ) ;
2019-09-22 15:14:34 +01:00
if ( ( rule_name_idx < 1 ) | | ( rule_name_idx > 6 ) ) { // Allow indexes 1 to 6
rule_name_idx = 1 ;
}
rule_name = rule_name . substring ( 0 , pos ) ; // "CURRENT"
str_value = root [ rule_task ] [ rule_name ] [ rule_name_idx - 1 ] ; // "ENERGY" and "CURRENT" and 0
} else {
str_value = root [ rule_task ] [ rule_name ] ; // "INA219" and "CURRENT"
}
2018-04-13 16:42:11 +01:00
2019-03-08 14:15:42 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"),
2019-08-17 13:27:41 +01:00
// rule_task.c_str(), rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");
2018-04-13 16:42:11 +01:00
2018-04-17 14:34:18 +01:00
if ( ! root [ rule_task ] [ rule_name ] . success ( ) ) { return false ; }
2018-04-13 16:42:11 +01:00
// No value but rule_name is ok
2019-08-17 13:27:41 +01:00
Rules . event_value = str_value ; // Prepare %value%
2018-04-13 16:42:11 +01:00
// Step 3: Compare rule (value)
2019-09-22 15:14:34 +01:00
float value = 0 ;
2018-04-13 16:42:11 +01:00
if ( str_value ) {
2019-07-01 17:20:43 +01:00
value = CharToFloat ( ( char * ) str_value ) ;
2018-08-13 12:09:22 +01:00
int int_value = int ( value ) ;
int int_rule_value = int ( rule_value ) ;
2019-09-09 16:24:27 +01:00
switch ( compareOperator ) {
2019-02-05 14:34:17 +00:00
case COMPARE_OPERATOR_EXACT_DIVISION :
match = ( int_rule_value & & ( int_value % int_rule_value ) = = 0 ) ;
break ;
case COMPARE_OPERATOR_EQUAL :
match = ( ! strcasecmp ( str_value , rule_svalue ) ) ; // Compare strings - this also works for hexadecimals
break ;
case COMPARE_OPERATOR_BIGGER :
match = ( value > rule_value ) ;
break ;
case COMPARE_OPERATOR_SMALLER :
match = ( value < rule_value ) ;
2018-08-13 12:09:22 +01:00
break ;
2019-02-05 14:34:17 +00:00
case COMPARE_OPERATOR_NUMBER_EQUAL :
match = ( value = = rule_value ) ;
2018-04-13 16:42:11 +01:00
break ;
2019-02-05 14:34:17 +00:00
case COMPARE_OPERATOR_NOT_EQUAL :
match = ( value ! = rule_value ) ;
2018-04-13 16:42:11 +01:00
break ;
2019-02-05 14:34:17 +00:00
case COMPARE_OPERATOR_BIGGER_EQUAL :
match = ( value > = rule_value ) ;
2018-04-13 16:42:11 +01:00
break ;
2019-02-05 14:34:17 +00:00
case COMPARE_OPERATOR_SMALLER_EQUAL :
match = ( value < = rule_value ) ;
2018-04-13 16:42:11 +01:00
break ;
2019-02-05 14:34:17 +00:00
default :
match = true ;
2018-04-13 16:42:11 +01:00
}
} else match = true ;
2018-07-16 10:34:44 +01:00
if ( bitRead ( Settings . rule_once , rule_set ) ) {
2018-04-13 16:42:11 +01:00
if ( match ) { // Only allow match state changes
2019-08-17 13:27:41 +01:00
if ( ! bitRead ( Rules . triggers [ rule_set ] , Rules . trigger_count [ rule_set ] ) ) {
bitSet ( Rules . triggers [ rule_set ] , Rules . trigger_count [ rule_set ] ) ;
2018-04-13 16:42:11 +01:00
} else {
match = false ;
}
} else {
2019-08-17 13:27:41 +01:00
bitClear ( Rules . triggers [ rule_set ] , Rules . trigger_count [ rule_set ] ) ;
2018-04-13 16:42:11 +01:00
}
}
return match ;
}
2019-09-09 16:24:27 +01:00
/********************************************************************************************/
/*
* Parse a comparison expression .
* Get 3 parts - left expression , compare operator and right expression .
* Input :
* expr - A comparison expression like VAR1 > = MEM1 + 10
* leftExpr - Used to accept returned left parts of expression
* rightExpr - Used to accept returned right parts of expression
* Output :
* leftExpr - Left parts of expression
* rightExpr - Right parts of expression
* Return :
* compare operator
* COMPARE_OPERATOR_NONE - failed
*/
int8_t parseCompareExpression ( String & expr , String & leftExpr , String & rightExpr )
{
char compare_operator [ 3 ] ;
int8_t compare = COMPARE_OPERATOR_NONE ;
2019-09-12 02:46:01 +01:00
leftExpr = expr ;
2019-09-09 16:24:27 +01:00
int position ;
for ( int8_t i = MAXIMUM_COMPARE_OPERATOR ; i > = 0 ; i - - ) {
snprintf_P ( compare_operator , sizeof ( compare_operator ) , kCompareOperators + ( i * 2 ) ) ;
if ( ( position = expr . indexOf ( compare_operator ) ) > 0 ) {
compare = i ;
leftExpr = expr . substring ( 0 , position ) ;
leftExpr . trim ( ) ;
rightExpr = expr . substring ( position + strlen ( compare_operator ) ) ;
rightExpr . trim ( ) ;
break ;
}
}
return compare ;
}
2019-11-01 15:05:07 +00:00
void RulesVarReplace ( String & commands , const String & sfind , const String & replace )
{
// String ufind = sfind;
// ufind.toUpperCase();
// char *find = (char*)ufind.c_str();
char * find = ( char * ) sfind . c_str ( ) ;
uint32_t flen = strlen ( find ) ;
String ucommand = commands ;
ucommand . toUpperCase ( ) ;
char * read_from = ( char * ) ucommand . c_str ( ) ;
char * write_to = ( char * ) commands . c_str ( ) ;
char * found_at ;
while ( ( found_at = strstr ( read_from , find ) ) ! = nullptr ) {
write_to + = ( found_at - read_from ) ;
memmove_P ( write_to , find , flen ) ; // Make variable Uppercase
write_to + = flen ;
read_from = found_at + flen ;
}
commands . replace ( find , replace ) ;
}
2018-04-13 16:42:11 +01:00
/*******************************************************************************************/
2019-01-28 13:08:33 +00:00
bool RuleSetProcess ( uint8_t rule_set , String & event_saved )
2018-04-13 16:42:11 +01:00
{
bool serviced = false ;
2018-04-27 17:06:19 +01:00
char stemp [ 10 ] ;
2018-04-13 16:42:11 +01:00
2018-05-06 15:07:42 +01:00
delay ( 0 ) ; // Prohibit possible loop software watchdog
2019-03-08 14:15:42 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event = %s, Rule = %s"), event_saved.c_str(), Settings.rules[rule_set]);
2018-05-13 16:38:44 +01:00
2018-05-24 13:25:52 +01:00
String rules = Settings . rules [ rule_set ] ;
2018-04-13 16:42:11 +01:00
2019-08-17 13:27:41 +01:00
Rules . trigger_count [ rule_set ] = 0 ;
2018-04-13 16:42:11 +01:00
int plen = 0 ;
2018-12-01 21:12:33 +00:00
int plen2 = 0 ;
bool stop_all_rules = false ;
2018-04-13 16:42:11 +01:00
while ( true ) {
2018-04-17 14:34:18 +01:00
rules = rules . substring ( plen ) ; // Select relative to last rule
2018-04-13 16:42:11 +01:00
rules . trim ( ) ;
2018-04-17 14:34:18 +01:00
if ( ! rules . length ( ) ) { return serviced ; } // No more rules
2018-04-13 16:42:11 +01:00
String rule = rules ;
2018-04-17 14:34:18 +01:00
rule . toUpperCase ( ) ; // "ON INA219#CURRENT>0.100 DO BACKLOG DIMMER 10;COLOR 100000 ENDON"
if ( ! rule . startsWith ( " ON " ) ) { return serviced ; } // Bad syntax - Nothing to start on
2018-04-13 16:42:11 +01:00
int pevt = rule . indexOf ( " DO " ) ;
2018-04-17 14:34:18 +01:00
if ( pevt = = - 1 ) { return serviced ; } // Bad syntax - Nothing to do
String event_trigger = rule . substring ( 3 , pevt ) ; // "INA219#CURRENT>0.100"
2018-04-13 16:42:11 +01:00
plen = rule . indexOf ( " ENDON " ) ;
2018-12-01 21:12:33 +00:00
plen2 = rule . indexOf ( " BREAK " ) ;
if ( ( plen = = - 1 ) & & ( plen2 = = - 1 ) ) { return serviced ; } // Bad syntax - No ENDON neither BREAK
if ( plen = = - 1 ) { plen = 9999 ; }
if ( plen2 = = - 1 ) { plen2 = 9999 ; }
2018-12-01 22:00:34 +00:00
plen = tmin ( plen , plen2 ) ;
2018-12-01 21:12:33 +00:00
2018-04-17 14:34:18 +01:00
String commands = rules . substring ( pevt + 4 , plen ) ; // "Backlog Dimmer 10;Color 100000"
2019-08-17 13:27:41 +01:00
Rules . event_value = " " ;
2018-05-13 16:38:44 +01:00
String event = event_saved ;
2018-04-13 16:42:11 +01:00
2019-03-08 14:15:42 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event |%s|, Rule |%s|, Command(s) |%s|"), event.c_str(), event_trigger.c_str(), commands.c_str());
2018-04-13 16:42:11 +01:00
2018-05-24 13:25:52 +01:00
if ( RulesRuleMatch ( rule_set , event , event_trigger ) ) {
2019-11-12 18:54:29 +00:00
if ( plen = = plen2 ) { stop_all_rules = true ; } // If BREAK was used on a triggered rule, Stop execution of this rule set
2018-04-27 17:06:19 +01:00
commands . trim ( ) ;
String ucommand = commands ;
ucommand . toUpperCase ( ) ;
2018-05-20 16:46:00 +01:00
// if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception
2019-12-11 01:36:23 +00:00
if ( ( ucommand . indexOf ( " EVENT " ) ! = - 1 ) & & ( ucommand . indexOf ( " BACKLOG " ) = = - 1 ) ) { commands = " backlog " + commands ; } // Always use Backlog with event to prevent rule event loop exception
2019-11-01 15:05:07 +00:00
RulesVarReplace ( commands , F ( " %VALUE% " ) , Rules . event_value ) ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_VARS ; i + + ) {
2019-11-01 15:05:07 +00:00
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%VAR%d%% " ) , i + 1 ) ;
RulesVarReplace ( commands , stemp , rules_vars [ i ] ) ;
2018-05-20 16:46:00 +01:00
}
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_MEMS ; i + + ) {
2019-11-01 15:05:07 +00:00
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %%MEM%d%% " ) , i + 1 ) ;
RulesVarReplace ( commands , stemp , Settings . mems [ i ] ) ;
2018-05-20 16:46:00 +01:00
}
2019-11-01 15:05:07 +00:00
RulesVarReplace ( commands , F ( " %TIME% " ) , String ( MinutesPastMidnight ( ) ) ) ;
RulesVarReplace ( commands , F ( " %UPTIME% " ) , String ( MinutesUptime ( ) ) ) ;
RulesVarReplace ( commands , F ( " %TIMESTAMP% " ) , GetDateAndTime ( DT_LOCAL ) ) ;
2019-12-04 20:11:03 +00:00
RulesVarReplace ( commands , F ( " %TOPIC% " ) , Settings . mqtt_topic ) ;
2018-06-26 15:22:53 +01:00
# if defined(USE_TIMERS) && defined(USE_SUNRISE)
2019-11-01 15:05:07 +00:00
RulesVarReplace ( commands , F ( " %SUNRISE% " ) , String ( SunMinutes ( 0 ) ) ) ;
RulesVarReplace ( commands , F ( " %SUNSET% " ) , String ( SunMinutes ( 1 ) ) ) ;
2018-06-26 15:22:53 +01:00
# endif // USE_TIMERS and USE_SUNRISE
2018-05-20 16:46:00 +01:00
char command [ commands . length ( ) + 1 ] ;
2019-03-08 18:24:02 +00:00
strlcpy ( command , commands . c_str ( ) , sizeof ( command ) ) ;
2018-04-13 16:42:11 +01:00
2019-03-08 14:15:42 +00:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " RUL: %s performs \" %s \" " ) , event_trigger . c_str ( ) , command ) ;
2018-04-13 16:42:11 +01:00
2019-03-23 16:00:59 +00:00
// Response_P(S_JSON_COMMAND_SVALUE, D_CMND_RULE, D_JSON_INITIATED);
2018-05-20 16:46:00 +01:00
// MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RULE));
2019-09-09 16:24:27 +01:00
# ifdef SUPPORT_IF_STATEMENT
char * pCmd = command ;
2019-11-01 15:05:07 +00:00
RulesPreprocessCommand ( pCmd ) ; // Do pre-process for IF statement
2019-09-09 16:24:27 +01:00
# endif
2018-05-28 14:52:42 +01:00
ExecuteCommand ( command , SRC_RULE ) ;
2018-04-13 16:42:11 +01:00
serviced = true ;
2019-11-01 15:05:07 +00:00
if ( stop_all_rules ) { return serviced ; } // If BREAK was used, Stop execution of this rule set
2018-04-13 16:42:11 +01:00
}
2019-11-12 18:54:29 +00:00
plen + = 6 ;
2019-08-17 13:27:41 +01:00
Rules . trigger_count [ rule_set ] + + ;
2018-04-13 16:42:11 +01:00
}
return serviced ;
}
/*******************************************************************************************/
2018-06-25 17:00:20 +01:00
bool RulesProcessEvent ( char * json_event )
2018-05-24 13:25:52 +01:00
{
bool serviced = false ;
2019-03-11 09:38:41 +00:00
# ifdef USE_DEBUG_DRIVER
2018-06-26 10:48:09 +01:00
ShowFreeMem ( PSTR ( " RulesProcessEvent " ) ) ;
2019-03-11 09:38:41 +00:00
# endif
2018-06-26 10:48:09 +01:00
2018-06-25 17:00:20 +01:00
String event_saved = json_event ;
2019-05-15 15:31:13 +01:00
// json_event = {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}}
// json_event = {"System":{"Boot":1}}
// json_event = {"SerialReceived":"on"} - invalid but will be expanded to {"SerialReceived":{"Data":"on"}}
char * p = strchr ( json_event , ' : ' ) ;
if ( ( p ! = NULL ) & & ! ( strchr ( + + p , ' : ' ) ) ) { // Find second colon
event_saved . replace ( F ( " : " ) , F ( " :{ \" Data \" : " ) ) ;
event_saved + = F ( " } " ) ;
// event_saved = {"SerialReceived":{"Data":"on"}}
}
2018-05-24 13:25:52 +01:00
event_saved . toUpperCase ( ) ;
2019-03-08 14:15:42 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event %s"), event_saved.c_str());
2018-06-28 11:25:50 +01:00
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_SETS ; i + + ) {
2018-05-24 13:25:52 +01:00
if ( strlen ( Settings . rules [ i ] ) & & bitRead ( Settings . rule_enabled , i ) ) {
if ( RuleSetProcess ( i , event_saved ) ) { serviced = true ; }
}
}
return serviced ;
}
2018-11-14 13:32:09 +00:00
bool RulesProcess ( void )
2018-06-25 17:00:20 +01:00
{
return RulesProcessEvent ( mqtt_data ) ;
}
2018-11-14 13:32:09 +00:00
void RulesInit ( void )
2018-04-13 16:42:11 +01:00
{
2018-06-25 17:00:20 +01:00
rules_flag . data = 0 ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_SETS ; i + + ) {
2018-05-24 13:25:52 +01:00
if ( Settings . rules [ i ] [ 0 ] = = ' \0 ' ) {
bitWrite ( Settings . rule_enabled , i , 0 ) ;
bitWrite ( Settings . rule_once , i , 0 ) ;
}
2018-04-13 16:42:11 +01:00
}
2019-08-17 13:27:41 +01:00
Rules . teleperiod = false ;
2018-04-13 16:42:11 +01:00
}
2018-11-14 13:32:09 +00:00
void RulesEvery50ms ( void )
2018-04-13 16:42:11 +01:00
{
2018-05-24 13:25:52 +01:00
if ( Settings . rule_enabled ) { // Any rule enabled
2018-06-25 17:00:20 +01:00
char json_event [ 120 ] ;
2019-08-17 13:27:41 +01:00
if ( - 1 = = Rules . new_power ) { Rules . new_power = power ; }
if ( Rules . new_power ! = Rules . old_power ) {
if ( Rules . old_power ! = - 1 ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < devices_present ; i + + ) {
2019-08-17 13:27:41 +01:00
uint8_t new_state = ( Rules . new_power > > i ) & 1 ;
if ( new_state ! = ( ( Rules . old_power > > i ) & 1 ) ) {
2018-06-25 17:00:20 +01:00
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Power%d \" :{ \" State \" :%d}} " ) , i + 1 , new_state ) ;
RulesProcessEvent ( json_event ) ;
2018-05-06 15:07:42 +01:00
}
}
2018-09-27 03:02:55 +01:00
} else {
// Boot time POWER OUTPUTS (Relays) Status
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < devices_present ; i + + ) {
2019-08-17 13:27:41 +01:00
uint8_t new_state = ( Rules . new_power > > i ) & 1 ;
2018-09-27 03:02:55 +01:00
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Power%d \" :{ \" Boot \" :%d}} " ) , i + 1 , new_state ) ;
RulesProcessEvent ( json_event ) ;
}
// Boot time SWITCHES Status
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_SWITCHES ; i + + ) {
2018-09-27 03:02:55 +01:00
# ifdef USE_TM1638
if ( ( pin [ GPIO_SWT1 + i ] < 99 ) | | ( ( pin [ GPIO_TM16CLK ] < 99 ) & & ( pin [ GPIO_TM16DIO ] < 99 ) & & ( pin [ GPIO_TM16STB ] < 99 ) ) ) {
# else
if ( pin [ GPIO_SWT1 + i ] < 99 ) {
2019-08-02 15:23:17 +01:00
# endif // USE_TM1638
2019-01-28 13:08:33 +00:00
bool swm = ( ( FOLLOW_INV = = Settings . switchmode [ i ] ) | | ( PUSHBUTTON_INV = = Settings . switchmode [ i ] ) | | ( PUSHBUTTONHOLD_INV = = Settings . switchmode [ i ] ) ) ;
2018-12-28 15:35:19 +00:00
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" " D_JSON_SWITCH " %d \" :{ \" Boot \" :%d}} " ) , i + 1 , ( swm ^ SwitchLastState ( i ) ) ) ;
2018-09-27 03:02:55 +01:00
RulesProcessEvent ( json_event ) ;
}
}
2018-05-06 15:07:42 +01:00
}
2019-08-17 13:27:41 +01:00
Rules . old_power = Rules . new_power ;
2018-05-29 16:24:42 +01:00
}
2019-08-17 13:27:41 +01:00
else if ( Rules . old_dimm ! = Settings . light_dimmer ) {
if ( Rules . old_dimm ! = - 1 ) {
2018-10-01 17:13:47 +01:00
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Dimmer \" :{ \" State \" :%d}} " ) , Settings . light_dimmer ) ;
} else {
// Boot time DIMMER VALUE
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Dimmer \" :{ \" Boot \" :%d}} " ) , Settings . light_dimmer ) ;
}
RulesProcessEvent ( json_event ) ;
2019-08-17 13:27:41 +01:00
Rules . old_dimm = Settings . light_dimmer ;
2018-10-01 17:13:47 +01:00
}
2019-08-17 13:27:41 +01:00
else if ( Rules . event_data [ 0 ] ) {
2018-05-29 16:24:42 +01:00
char * event ;
char * parameter ;
2019-08-17 13:27:41 +01:00
event = strtok_r ( Rules . event_data , " = " , & parameter ) ; // Rules.event_data = fanspeed=10
2018-05-29 16:24:42 +01:00
if ( event ) {
event = Trim ( event ) ;
if ( parameter ) {
parameter = Trim ( parameter ) ;
} else {
parameter = event + strlen ( event ) ; // '\0'
}
2018-06-25 17:00:20 +01:00
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Event \" :{ \" %s \" : \" %s \" }} " ) , event , parameter ) ;
2019-08-17 13:27:41 +01:00
Rules . event_data [ 0 ] = ' \0 ' ;
2018-06-25 17:00:20 +01:00
RulesProcessEvent ( json_event ) ;
2018-05-29 16:24:42 +01:00
} else {
2019-08-17 13:27:41 +01:00
Rules . event_data [ 0 ] = ' \0 ' ;
2018-05-29 16:24:42 +01:00
}
}
2019-08-17 13:27:41 +01:00
else if ( Rules . vars_event | | Rules . mems_event ) {
if ( Rules . vars_event ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_VARS ; i + + ) {
2019-08-17 13:27:41 +01:00
if ( bitRead ( Rules . vars_event , i ) ) {
bitClear ( Rules . vars_event , i ) ;
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Var%d \" :{ \" State \" :%s}} " ) , i + 1 , rules_vars [ i ] ) ;
2019-03-29 08:42:50 +00:00
RulesProcessEvent ( json_event ) ;
break ;
}
2019-01-04 19:26:37 +00:00
}
}
2019-08-17 13:27:41 +01:00
if ( Rules . mems_event ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_MEMS ; i + + ) {
2019-08-17 13:27:41 +01:00
if ( bitRead ( Rules . mems_event , i ) ) {
bitClear ( Rules . mems_event , i ) ;
2019-03-29 08:42:50 +00:00
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Mem%d \" :{ \" State \" :%s}} " ) , i + 1 , Settings . mems [ i ] ) ;
RulesProcessEvent ( json_event ) ;
break ;
}
2019-01-04 19:26:37 +00:00
}
}
}
2018-06-25 17:00:20 +01:00
else if ( rules_flag . data ) {
uint16_t mask = 1 ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULES_FLAG ; i + + ) {
2018-06-25 17:00:20 +01:00
if ( rules_flag . data & mask ) {
rules_flag . data ^ = mask ;
json_event [ 0 ] = ' \0 ' ;
switch ( i ) {
case 0 : strncpy_P ( json_event , PSTR ( " { \" System \" :{ \" Boot \" :1}} " ) , sizeof ( json_event ) ) ; break ;
2019-02-19 13:49:15 +00:00
case 1 : snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Time \" :{ \" Initialized \" :%d}} " ) , MinutesPastMidnight ( ) ) ; break ;
case 2 : snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Time \" :{ \" Set \" :%d}} " ) , MinutesPastMidnight ( ) ) ; break ;
2018-06-25 17:00:20 +01:00
case 3 : strncpy_P ( json_event , PSTR ( " { \" MQTT \" :{ \" Connected \" :1}} " ) , sizeof ( json_event ) ) ; break ;
case 4 : strncpy_P ( json_event , PSTR ( " { \" MQTT \" :{ \" Disconnected \" :1}} " ) , sizeof ( json_event ) ) ; break ;
2018-07-28 14:06:31 +01:00
case 5 : strncpy_P ( json_event , PSTR ( " { \" WIFI \" :{ \" Connected \" :1}} " ) , sizeof ( json_event ) ) ; break ;
case 6 : strncpy_P ( json_event , PSTR ( " { \" WIFI \" :{ \" Disconnected \" :1}} " ) , sizeof ( json_event ) ) ; break ;
Add command WebColor
* Add rule Http#Initialized
* Add command WebColor to change non-persistent GUI colors on the fly
Use a rule like:
rule3 on http#initialized do webcolor {"webcolor":["#eeeeee","#181818","#4f4f4f","#000000","#dddddd","#008000","#222222","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999","#000000"]} endon
or
rule3 on http#initialized do webcolor {"webcolor":["#eee","#181818","#4f4f4f","#000","#ddd","#008000","#222"]} endon
to make color changes persistent)
2019-04-08 21:37:39 +01:00
case 7 : strncpy_P ( json_event , PSTR ( " { \" HTTP \" :{ \" Initialized \" :1}} " ) , sizeof ( json_event ) ) ; break ;
2019-09-29 17:00:01 +01:00
# ifdef USE_SHUTTER
case 8 : strncpy_P ( json_event , PSTR ( " { \" SHUTTER \" :{ \" Moved \" :1}} " ) , sizeof ( json_event ) ) ; break ;
case 9 : strncpy_P ( json_event , PSTR ( " { \" SHUTTER \" :{ \" Moving \" :1}} " ) , sizeof ( json_event ) ) ; break ;
# endif // USE_SHUTTER
2018-06-25 17:00:20 +01:00
}
if ( json_event [ 0 ] ) {
RulesProcessEvent ( json_event ) ;
break ; // Only service one event within 50mS
}
}
mask < < = 1 ;
}
}
2018-08-26 16:10:18 +01:00
}
}
2018-11-18 14:33:13 +00:00
uint8_t rules_xsns_index = 0 ;
2018-11-14 13:32:09 +00:00
void RulesEvery100ms ( void )
2018-08-26 16:10:18 +01:00
{
2018-09-26 10:56:58 +01:00
if ( Settings . rule_enabled & & ( uptime > 4 ) ) { // Any rule enabled and allow 4 seconds start-up time for sensors (#3811)
2018-08-26 16:10:18 +01:00
mqtt_data [ 0 ] = ' \0 ' ;
2018-09-08 16:18:31 +01:00
int tele_period_save = tele_period ;
2018-11-18 14:33:13 +00:00
tele_period = 2 ; // Do not allow HA updates during next function call
XsnsNextCall ( FUNC_JSON_APPEND , rules_xsns_index ) ; // ,"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}
2018-08-26 16:10:18 +01:00
tele_period = tele_period_save ;
if ( strlen ( mqtt_data ) ) {
2018-11-18 14:33:13 +00:00
mqtt_data [ 0 ] = ' { ' ; // {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
ResponseJsonEnd ( ) ;
2018-08-26 16:10:18 +01:00
RulesProcess ( ) ;
2018-04-13 16:42:11 +01:00
}
}
}
2018-11-14 13:32:09 +00:00
void RulesEverySecond ( void )
2018-04-13 16:42:11 +01:00
{
2018-05-24 13:25:52 +01:00
if ( Settings . rule_enabled ) { // Any rule enabled
2018-06-25 17:00:20 +01:00
char json_event [ 120 ] ;
2018-06-26 15:22:53 +01:00
if ( RtcTime . valid ) {
2019-08-17 13:27:41 +01:00
if ( ( uptime > 60 ) & & ( RtcTime . minute ! = Rules . last_minute ) ) { // Execute from one minute after restart every minute only once
Rules . last_minute = RtcTime . minute ;
2019-02-19 13:49:15 +00:00
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Time \" :{ \" Minute \" :%d}} " ) , MinutesPastMidnight ( ) ) ;
2018-06-26 15:22:53 +01:00
RulesProcessEvent ( json_event ) ;
}
}
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_TIMERS ; i + + ) {
2019-08-17 13:27:41 +01:00
if ( Rules . timer [ i ] ! = 0L ) { // Timer active?
if ( TimeReached ( Rules . timer [ i ] ) ) { // Timer finished?
Rules . timer [ i ] = 0L ; // Turn off this timer
2018-06-25 17:00:20 +01:00
snprintf_P ( json_event , sizeof ( json_event ) , PSTR ( " { \" Rules \" :{ \" Timer \" :%d}} " ) , i + 1 ) ;
RulesProcessEvent ( json_event ) ;
2018-04-13 16:42:11 +01:00
}
}
}
}
}
2019-05-19 11:42:10 +01:00
void RulesSaveBeforeRestart ( void )
{
if ( Settings . rule_enabled ) { // Any rule enabled
char json_event [ 32 ] ;
strncpy_P ( json_event , PSTR ( " { \" System \" :{ \" Save \" :1}} " ) , sizeof ( json_event ) ) ;
RulesProcessEvent ( json_event ) ;
}
}
2018-11-14 13:32:09 +00:00
void RulesSetPower ( void )
2018-05-06 15:07:42 +01:00
{
2019-08-17 13:27:41 +01:00
Rules . new_power = XdrvMailbox . index ;
2018-05-06 15:07:42 +01:00
}
2018-11-14 13:32:09 +00:00
void RulesTeleperiod ( void )
2018-04-27 17:06:19 +01:00
{
2019-08-17 13:27:41 +01:00
Rules . teleperiod = true ;
2018-04-27 17:06:19 +01:00
RulesProcess ( ) ;
2019-08-17 13:27:41 +01:00
Rules . teleperiod = false ;
2018-04-27 17:06:19 +01:00
}
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
# ifdef SUPPORT_MQTT_EVENT
/********************************************************************************************/
/*
* Rules : Process received MQTT message .
* If the message is in our subscription list , trigger an event with the value parsed from MQTT data
* Input :
* void - We are going to access XdrvMailbox data directly .
* Return :
* true - The message is consumed .
* false - The message is not in our list .
*/
bool RulesMqttData ( void )
{
2019-09-15 02:35:55 +01:00
if ( XdrvMailbox . data_len < 1 | | XdrvMailbox . data_len > 256 ) {
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
return false ;
}
2019-10-02 11:51:37 +01:00
bool serviced = false ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
String sTopic = XdrvMailbox . topic ;
String sData = XdrvMailbox . data ;
2019-03-08 14:15:42 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: MQTT Topic %s, Event %s"), XdrvMailbox.topic, XdrvMailbox.data);
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
MQTT_Subscription event_item ;
//Looking for matched topic
2019-06-30 15:44:36 +01:00
for ( uint32_t index = 0 ; index < subscriptions . size ( ) ; index + + ) {
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
event_item = subscriptions . get ( index ) ;
2019-03-08 14:15:42 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Match MQTT message Topic %s with subscription topic %s"), sTopic.c_str(), event_item.Topic.c_str());
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
if ( sTopic . startsWith ( event_item . Topic ) ) {
//This topic is subscribed by us, so serve it
serviced = true ;
String value ;
if ( event_item . Key . length ( ) = = 0 ) { //If did not specify Key
value = sData ;
} else { //If specified Key, need to parse Key/Value from JSON data
2019-09-15 02:35:55 +01:00
StaticJsonBuffer < 500 > jsonBuf ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
JsonObject & jsonData = jsonBuf . parseObject ( sData ) ;
String key1 = event_item . Key ;
String key2 ;
if ( ! jsonData . success ( ) ) break ; //Failed to parse JSON data, ignore this message.
int dot ;
if ( ( dot = key1 . indexOf ( ' . ' ) ) > 0 ) {
key2 = key1 . substring ( dot + 1 ) ;
key1 = key1 . substring ( 0 , dot ) ;
if ( ! jsonData [ key1 ] [ key2 ] . success ( ) ) break ; //Failed to get the key/value, ignore this message.
value = ( const char * ) jsonData [ key1 ] [ key2 ] ;
} else {
if ( ! jsonData [ key1 ] . success ( ) ) break ;
value = ( const char * ) jsonData [ key1 ] ;
}
}
value . trim ( ) ;
//Create an new event. Cannot directly call RulesProcessEvent().
2019-08-17 13:27:41 +01:00
snprintf_P ( Rules . event_data , sizeof ( Rules . event_data ) , PSTR ( " %s=%s " ) , event_item . Event . c_str ( ) , value . c_str ( ) ) ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
}
}
return serviced ;
}
/********************************************************************************************/
/*
* Subscribe a MQTT topic ( with or without key ) and assign an event name to it
* Command Subscribe format :
* Subscribe < event_name > , < topic > [ , < key > ]
* This command will subscribe a < topic > and give it an event name < event_name > .
* The optional parameter < key > is for parse the specified key / value from MQTT message
* payload with JSON format .
* Subscribe
* Subscribe command without any parameter will list all topics currently subscribed .
* Input :
2019-08-02 15:23:17 +01:00
* XdrvMailbox . data - A char buffer with all the parameters
* XdrvMailbox . data_len - Length of the parameters
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
* Return :
* A string include subscribed event , topic and key .
*/
2019-08-02 15:23:17 +01:00
void CmndSubscribe ( void )
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
{
MQTT_Subscription subscription_item ;
String events ;
2019-08-02 15:23:17 +01:00
if ( XdrvMailbox . data_len > 0 ) {
char parameters [ XdrvMailbox . data_len + 1 ] ;
memcpy ( parameters , XdrvMailbox . data , XdrvMailbox . data_len ) ;
parameters [ XdrvMailbox . data_len ] = ' \0 ' ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
String event_name , topic , key ;
char * pos = strtok ( parameters , " , " ) ;
if ( pos ) {
event_name = Trim ( pos ) ;
2019-03-26 17:26:50 +00:00
pos = strtok ( nullptr , " , " ) ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
if ( pos ) {
topic = Trim ( pos ) ;
2019-03-26 17:26:50 +00:00
pos = strtok ( nullptr , " , " ) ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
if ( pos ) {
key = Trim ( pos ) ;
}
}
}
2019-03-08 14:15:42 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Subscribe command with parameters: %s, %s, %s."), event_name.c_str(), topic.c_str(), key.c_str());
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
event_name . toUpperCase ( ) ;
if ( event_name . length ( ) > 0 & & topic . length ( ) > 0 ) {
//Search all subscriptions
2019-06-30 15:44:36 +01:00
for ( uint32_t index = 0 ; index < subscriptions . size ( ) ; index + + ) {
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
if ( subscriptions . get ( index ) . Event . equals ( event_name ) ) {
//If find exists one, remove it.
String stopic = subscriptions . get ( index ) . Topic + " /# " ;
MqttUnsubscribe ( stopic . c_str ( ) ) ;
subscriptions . remove ( index ) ;
break ;
}
}
//Add "/#" to the topic
if ( ! topic . endsWith ( " # " ) ) {
if ( topic . endsWith ( " / " ) ) {
topic . concat ( " # " ) ;
} else {
topic . concat ( " /# " ) ;
}
}
2019-03-08 14:15:42 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: New topic: %s."), topic.c_str());
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
//MQTT Subscribe
subscription_item . Event = event_name ;
subscription_item . Topic = topic . substring ( 0 , topic . length ( ) - 2 ) ; //Remove "/#" so easy to match
subscription_item . Key = key ;
subscriptions . add ( subscription_item ) ;
MqttSubscribe ( topic . c_str ( ) ) ;
events . concat ( event_name + " , " + topic
+ ( key . length ( ) > 0 ? " , " : " " )
+ key ) ;
} else {
events = D_JSON_WRONG_PARAMETERS ;
}
} else {
//If did not specify the event name, list all subscribed event
2019-06-30 15:44:36 +01:00
for ( uint32_t index = 0 ; index < subscriptions . size ( ) ; index + + ) {
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
subscription_item = subscriptions . get ( index ) ;
events . concat ( subscription_item . Event + " , " + subscription_item . Topic
+ ( subscription_item . Key . length ( ) > 0 ? " , " : " " )
+ subscription_item . Key + " ; " ) ;
}
}
2019-08-03 12:01:34 +01:00
ResponseCmndChar ( events . c_str ( ) ) ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
}
/********************************************************************************************/
/*
* Unsubscribe specified MQTT event . If no event specified , Unsubscribe all .
* Command Unsubscribe format :
2019-08-02 15:23:17 +01:00
* UnSubscribe [ < event_name > ]
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
* Input :
2019-08-02 15:23:17 +01:00
* XdrvMailbox . data - Event name
* XdrvMailbox . data_len - Length of the parameters
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
* Return :
* list all the events unsubscribed .
*/
2019-08-02 15:23:17 +01:00
void CmndUnsubscribe ( void )
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
{
MQTT_Subscription subscription_item ;
String events ;
2019-08-02 15:23:17 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t index = 0 ; index < subscriptions . size ( ) ; index + + ) {
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
subscription_item = subscriptions . get ( index ) ;
2019-08-02 15:23:17 +01:00
if ( subscription_item . Event . equalsIgnoreCase ( XdrvMailbox . data ) ) {
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
String stopic = subscription_item . Topic + " /# " ;
MqttUnsubscribe ( stopic . c_str ( ) ) ;
events = subscription_item . Event ;
subscriptions . remove ( index ) ;
break ;
}
}
} else {
2019-08-02 15:23:17 +01:00
// If did not specify the event name, unsubscribe all event
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
String stopic ;
while ( subscriptions . size ( ) > 0 ) {
events . concat ( subscriptions . get ( 0 ) . Event + " ; " ) ;
stopic = subscriptions . get ( 0 ) . Topic + " /# " ;
MqttUnsubscribe ( stopic . c_str ( ) ) ;
subscriptions . remove ( 0 ) ;
}
}
2019-08-03 12:01:34 +01:00
ResponseCmndChar ( events . c_str ( ) ) ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
}
2019-08-02 15:23:17 +01:00
# endif // SUPPORT_MQTT_EVENT
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# ifdef USE_EXPRESSION
2019-09-09 16:24:27 +01:00
/********************************************************************************************/
/*
* Looking for matched bracket - " ) "
* Search buffer from current loction , skip all nested bracket pairs , find the matched close bracket .
* Input :
* pStart - Point to a char buffer start with " ( "
* Output :
* N / A
* Return :
* position of matched close bracket
*/
char * findClosureBracket ( char * pStart )
{
char * pointer = pStart + 1 ;
//Look for the matched closure parenthesis.")"
bool bFindClosures = false ;
uint8_t matchClosures = 1 ;
while ( * pointer )
{
if ( * pointer = = ' ) ' ) {
matchClosures - - ;
if ( matchClosures = = 0 ) {
bFindClosures = true ;
break ;
}
} else if ( * pointer = = ' ( ' ) {
matchClosures + + ;
}
pointer + + ;
}
if ( bFindClosures ) {
return pointer ;
} else {
2019-09-10 10:05:53 +01:00
return nullptr ;
2019-09-09 16:24:27 +01:00
}
}
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
/********************************************************************************************/
/*
* Parse a number value
* Input :
* pNumber - A char pointer point to a digit started string ( guaranteed )
2019-07-01 17:20:43 +01:00
* value - Reference a float variable used to accept the result
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Output :
* pNumber - Pointer forward to next character after the number
2019-07-01 17:20:43 +01:00
* value - float type , the result value
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Return :
* true - succeed
* false - failed
*/
2019-07-01 17:20:43 +01:00
bool findNextNumber ( char * & pNumber , float & value )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
{
bool bSucceed = false ;
String sNumber = " " ;
2019-09-28 02:58:32 +01:00
if ( * pNumber = = ' - ' ) {
sNumber = " - " ;
pNumber + + ;
}
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
while ( * pNumber ) {
if ( isdigit ( * pNumber ) | | ( * pNumber = = ' . ' ) ) {
sNumber + = * pNumber ;
pNumber + + ;
} else {
break ;
}
}
if ( sNumber . length ( ) > 0 ) {
2019-07-01 17:20:43 +01:00
value = CharToFloat ( sNumber . c_str ( ) ) ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
bSucceed = true ;
}
return bSucceed ;
}
/********************************************************************************************/
/*
2019-07-01 17:20:43 +01:00
* Parse a variable ( like VAR1 , MEM3 ) and get its value ( float type )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Input :
* pVarname - A char pointer point to a variable name string
2019-07-01 17:20:43 +01:00
* value - Reference a float variable used to accept the result
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Output :
* pVarname - Pointer forward to next character after the variable
2019-07-01 17:20:43 +01:00
* value - float type , the result value
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Return :
* true - succeed
* false - failed
*/
2019-07-01 17:20:43 +01:00
bool findNextVariableValue ( char * & pVarname , float & value )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
{
2019-02-19 13:49:15 +00:00
bool succeed = true ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
value = 0 ;
String sVarName = " " ;
while ( * pVarname ) {
if ( isalpha ( * pVarname ) | | isdigit ( * pVarname ) ) {
sVarName . concat ( * pVarname ) ;
pVarname + + ;
} else {
break ;
}
}
sVarName . toUpperCase ( ) ;
2019-02-19 13:49:15 +00:00
if ( sVarName . startsWith ( F ( " VAR " ) ) ) {
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
int index = sVarName . substring ( 3 ) . toInt ( ) ;
if ( index > 0 & & index < = MAX_RULE_VARS ) {
2019-08-17 13:27:41 +01:00
value = CharToFloat ( rules_vars [ index - 1 ] ) ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
}
2019-02-19 13:49:15 +00:00
} else if ( sVarName . startsWith ( F ( " MEM " ) ) ) {
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
int index = sVarName . substring ( 3 ) . toInt ( ) ;
if ( index > 0 & & index < = MAX_RULE_MEMS ) {
2019-07-01 17:20:43 +01:00
value = CharToFloat ( Settings . mems [ index - 1 ] ) ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
}
2019-02-19 13:49:15 +00:00
} else if ( sVarName . equals ( F ( " TIME " ) ) ) {
value = MinutesPastMidnight ( ) ;
} else if ( sVarName . equals ( F ( " UPTIME " ) ) ) {
value = MinutesUptime ( ) ;
} else if ( sVarName . equals ( F ( " UTCTIME " ) ) ) {
value = UtcTime ( ) ;
} else if ( sVarName . equals ( F ( " LOCALTIME " ) ) ) {
value = LocalTime ( ) ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# if defined(USE_TIMERS) && defined(USE_SUNRISE)
2019-02-19 13:49:15 +00:00
} else if ( sVarName . equals ( F ( " SUNRISE " ) ) ) {
value = SunMinutes ( 0 ) ;
} else if ( sVarName . equals ( F ( " SUNSET " ) ) ) {
value = SunMinutes ( 1 ) ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# endif
2019-02-19 13:49:15 +00:00
} else {
succeed = false ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
}
return succeed ;
}
/********************************************************************************************/
/*
* Find next object in expression and evaluate it
* An object could be :
2019-09-28 02:58:32 +01:00
* - A float number start with a digit or minus , like 0.787 , - 3
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* - A variable name , like VAR1 , MEM3
* - An expression enclosed with a pair of round brackets , ( . . . . . )
* Input :
* pointer - A char pointer point to a place of the expression string
2019-07-01 17:20:43 +01:00
* value - Reference a float variable used to accept the result
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Output :
* pointer - Pointer forward to next character after next object
2019-07-01 17:20:43 +01:00
* value - float type , the result value
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Return :
* true - succeed
* false - failed
*/
2019-07-01 17:20:43 +01:00
bool findNextObjectValue ( char * & pointer , float & value )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
{
bool bSucceed = false ;
2019-02-16 15:17:17 +00:00
while ( * pointer )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
{
if ( isspace ( * pointer ) ) { //Skip leading spaces
pointer + + ;
continue ;
}
2019-09-28 02:58:32 +01:00
if ( isdigit ( * pointer ) | | ( * pointer ) = = ' - ' ) { //This object is a number
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
bSucceed = findNextNumber ( pointer , value ) ;
break ;
} else if ( isalpha ( * pointer ) ) { //Should be a variable like VAR12, MEM1
bSucceed = findNextVariableValue ( pointer , value ) ;
break ;
} else if ( * pointer = = ' ( ' ) { //It is a sub expression bracketed with ()
2019-09-09 16:24:27 +01:00
char * closureBracket = findClosureBracket ( pointer ) ; //Get the position of closure bracket ")"
2019-09-10 10:05:53 +01:00
if ( closureBracket ! = nullptr ) {
2019-09-28 03:15:42 +01:00
value = evaluateExpression ( pointer + 1 , closureBracket - pointer - 1 ) ;
2019-09-09 16:24:27 +01:00
pointer = closureBracket + 1 ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
bSucceed = true ;
}
break ;
} else { //No number, no variable, no expression, then invalid object.
break ;
}
}
return bSucceed ;
}
/********************************************************************************************/
/*
* Find next operator in expression
* An operator could be : + , - , * , / , % , ^
* Input :
* pointer - A char pointer point to a place of the expression string
* op - Reference to a variable used to accept the result
* Output :
* pointer - Pointer forward to next character after next operator
* op - The operator . 0 , 1 , 2 , 3 , 4 , 5
* Return :
* true - succeed
* false - failed
*/
bool findNextOperator ( char * & pointer , int8_t & op )
{
bool bSucceed = false ;
2019-02-16 15:17:17 +00:00
while ( * pointer )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
{
if ( isspace ( * pointer ) ) { //Skip leading spaces
pointer + + ;
continue ;
}
2019-09-28 02:52:33 +01:00
op = EXPRESSION_OPERATOR_ADD ;
const char * pch = kExpressionOperators ;
char ch ;
while ( ( ch = pgm_read_byte ( pch + + ) ) ! = ' \0 ' ) {
if ( ch = = * pointer ) {
bSucceed = true ;
pointer + + ;
break ;
}
op + + ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
}
break ;
}
return bSucceed ;
}
/********************************************************************************************/
/*
* Calculate a simple expression composed by 2 value and 1 operator , like 2 * 3
* Input :
* pointer - A char pointer point to a place of the expression string
2019-07-01 17:20:43 +01:00
* value - Reference a float variable used to accept the result
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Output :
* pointer - Pointer forward to next character after next object
2019-07-01 17:20:43 +01:00
* value - float type , the result value
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* Return :
* true - succeed
* false - failed
*/
2019-07-01 17:20:43 +01:00
float calculateTwoValues ( float v1 , float v2 , uint8_t op )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
{
switch ( op )
{
case EXPRESSION_OPERATOR_ADD :
return v1 + v2 ;
case EXPRESSION_OPERATOR_SUBTRACT :
return v1 - v2 ;
case EXPRESSION_OPERATOR_MULTIPLY :
return v1 * v2 ;
case EXPRESSION_OPERATOR_DIVIDEDBY :
return ( 0 = = v2 ) ? 0 : ( v1 / v2 ) ;
case EXPRESSION_OPERATOR_MODULO :
return ( 0 = = v2 ) ? 0 : ( int ( v1 ) % int ( v2 ) ) ;
case EXPRESSION_OPERATOR_POWER :
return FastPrecisePow ( v1 , v2 ) ;
}
return 0 ;
}
/********************************************************************************************/
/*
* Parse and evaluate an expression .
* For example : " 10 * ( MEM2 + 1) / 2 "
* Right now , only support operators listed here : ( order by priority )
* Priority 4 : ^ ( power )
* Priority 3 : % ( modulo , always get integer result )
* Priority 2 : * , /
* Priority 1 : + , -
* Input :
* expression - The expression to be evaluated
* len - Length of the expression
* Return :
2019-07-01 17:20:43 +01:00
* float - result .
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
* 0 - if the expression is invalid
* An example :
* MEM1 = 3 , MEM2 = 6 , VAR2 = 15 , VAR10 = 80
* At beginning , the expression might be complicated like : 3.14 * ( MEM1 * ( 10 + VAR2 ^ 2 ) - 100 ) % 10 + VAR10 / ( 2 + MEM2 )
* We are going to scan the whole expression , evaluate each object .
* Finally we will have a value list : .
* Order Object Value
* 0 3.14 3.14
* 1 ( MEM1 * ( 10 + VAR2 ^ 2 ) - 100 ) 605
* 2 10 10
* 3 VAR10 80
* 4 ( 2 + MEM2 ) 8
* And an operator list :
* Order Operator Priority
* 0 * 2
* 1 % 3
* 2 + 1
* 3 / 2
*/
2019-07-01 17:20:43 +01:00
float evaluateExpression ( const char * expression , unsigned int len )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
{
char expbuf [ len + 1 ] ;
memcpy ( expbuf , expression , len ) ;
expbuf [ len ] = ' \0 ' ;
char * scan_pointer = expbuf ;
2019-07-01 17:20:43 +01:00
LinkedList < float > object_values ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
LinkedList < int8_t > operators ;
int8_t op ;
2019-07-01 17:20:43 +01:00
float va ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
//Find and add the value of first object
if ( findNextObjectValue ( scan_pointer , va ) ) {
object_values . add ( va ) ;
} else {
return 0 ;
}
while ( * scan_pointer )
{
2019-02-16 15:17:17 +00:00
if ( findNextOperator ( scan_pointer , op )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
& & * scan_pointer
2019-02-16 15:17:17 +00:00
& & findNextObjectValue ( scan_pointer , va ) )
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
{
operators . add ( op ) ;
object_values . add ( va ) ;
} else {
//No operator followed or no more object after this operator, we done.
2019-02-16 15:17:17 +00:00
break ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
}
}
//Going to evaluate the whole expression
//Calculate by order of operator priorities. Looking for all operators with specified priority (from High to Low)
2019-06-30 15:44:36 +01:00
for ( int32_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY ; priority > 0 ; priority - - ) {
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
int index = 0 ;
while ( index < operators . size ( ) ) {
2019-09-28 02:52:33 +01:00
if ( priority = = pgm_read_byte ( kExpressionOperatorsPriorities + operators . get ( index ) ) ) { //need to calculate the operator first
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
//get current object value and remove the next object with current operator
va = calculateTwoValues ( object_values . get ( index ) , object_values . remove ( index + 1 ) , operators . remove ( index ) ) ;
//Replace the current value with the result
object_values . set ( index , va ) ;
} else {
index + + ;
}
}
}
return object_values . get ( 0 ) ;
}
2019-08-02 15:23:17 +01:00
# endif // USE_EXPRESSION
2019-09-09 16:24:27 +01:00
# ifdef SUPPORT_IF_STATEMENT
2019-11-20 19:53:12 +00:00
void CmndIf ( void )
2019-09-09 16:24:27 +01:00
{
if ( XdrvMailbox . data_len > 0 ) {
char parameters [ XdrvMailbox . data_len + 1 ] ;
memcpy ( parameters , XdrvMailbox . data , XdrvMailbox . data_len ) ;
parameters [ XdrvMailbox . data_len ] = ' \0 ' ;
ProcessIfStatement ( parameters ) ;
}
2019-09-15 02:39:21 +01:00
ResponseCmndDone ( ) ;
2019-09-09 16:24:27 +01:00
}
/********************************************************************************************/
/*
* Evaluate a comparison expression .
* Get the logic value of expression , true or false
* Input :
* expression - A comparison expression like VAR1 > = MEM1 + 10
* len - Length of expression
* Output :
* N / A
* Return :
* logic value of comparison expression
*/
bool evaluateComparisonExpression ( const char * expression , int len )
{
bool bResult = true ;
char expbuf [ len + 1 ] ;
memcpy ( expbuf , expression , len ) ;
expbuf [ len ] = ' \0 ' ;
String compare_expression = expbuf ;
String leftExpr , rightExpr ;
int8_t compareOp = parseCompareExpression ( compare_expression , leftExpr , rightExpr ) ;
double leftValue = evaluateExpression ( leftExpr . c_str ( ) , leftExpr . length ( ) ) ;
double rightValue = evaluateExpression ( rightExpr . c_str ( ) , rightExpr . length ( ) ) ;
switch ( compareOp ) {
case COMPARE_OPERATOR_EXACT_DIVISION :
bResult = ( rightValue ! = 0 & & leftValue = = int ( leftValue )
& & rightValue = = int ( rightValue ) & & ( int ( leftValue ) % int ( rightValue ) ) = = 0 ) ;
break ;
case COMPARE_OPERATOR_EQUAL :
bResult = leftExpr . equalsIgnoreCase ( rightExpr ) ; // Compare strings - this also works for hexadecimals
break ;
case COMPARE_OPERATOR_BIGGER :
bResult = ( leftValue > rightValue ) ;
break ;
case COMPARE_OPERATOR_SMALLER :
bResult = ( leftValue < rightValue ) ;
break ;
case COMPARE_OPERATOR_NUMBER_EQUAL :
bResult = ( leftValue = = rightValue ) ;
break ;
case COMPARE_OPERATOR_NOT_EQUAL :
bResult = ( leftValue ! = rightValue ) ;
break ;
case COMPARE_OPERATOR_BIGGER_EQUAL :
bResult = ( leftValue > = rightValue ) ;
break ;
case COMPARE_OPERATOR_SMALLER_EQUAL :
bResult = ( leftValue < = rightValue ) ;
break ;
}
return bResult ;
}
/********************************************************************************************/
/*
* Looking for a logical operator , either " AND " or " OR "
* A logical operator is expected at this moment . If we find something else , this function will fail .
* Input :
* pointer - Point to a char buffer
* op - Used to accpet the logical operator type
* Output :
* Pointer - pointer will forward to next character after the logical operator .
* op - The logical operator type we found
* Return :
* true - succeed
* false - failed
*/
bool findNextLogicOperator ( char * & pointer , int8_t & op )
{
bool bSucceed = false ;
while ( * pointer & & isspace ( * pointer ) ) {
//Skip spaces
pointer + + ;
}
if ( * pointer ) {
if ( strncasecmp_P ( pointer , PSTR ( " AND " ) , 4 ) = = 0 ) {
op = LOGIC_OPERATOR_AND ;
pointer + = 4 ;
bSucceed = true ;
} else if ( strncasecmp_P ( pointer , PSTR ( " OR " ) , 3 ) = = 0 ) {
op = LOGIC_OPERATOR_OR ;
pointer + = 3 ;
bSucceed = true ;
}
}
return bSucceed ;
}
/********************************************************************************************/
/*
* Find next logical object and get its value
* A logical object could be :
* - A comparison expression .
* - A logical expression bracketed with a pair of parenthesis .
* Input :
* pointer - A char pointer point to a start of logical object
* value - Used to accept the result value
* Output :
* pointer - Pointer forward to next character after the object
* value - boolean type , the value of the logical object .
* Return :
* true - succeed
* false - failed
*/
bool findNextLogicObjectValue ( char * & pointer , bool & value )
{
bool bSucceed = false ;
while ( * pointer & & isspace ( * pointer ) ) {
//Skip leading spaces
pointer + + ;
}
char * pExpr = pointer ;
while ( * pointer ) {
if ( isalpha ( * pointer )
& & ( strncasecmp_P ( pointer , PSTR ( " AND " ) , 4 ) = = 0
| | strncasecmp_P ( pointer , PSTR ( " OR " ) , 3 ) = = 0 ) )
{ //We have a logic operator, should stop
value = evaluateComparisonExpression ( pExpr , pointer - pExpr ) ;
bSucceed = true ;
break ;
} else if ( * pointer = = ' ( ' ) { //It is a sub expression bracketed with ()
char * closureBracket = findClosureBracket ( pointer ) ; //Get the position of closure bracket ")"
2019-09-10 10:05:53 +01:00
if ( closureBracket ! = nullptr ) {
2019-09-28 03:15:42 +01:00
value = evaluateLogicalExpression ( pointer + 1 , closureBracket - pointer - 1 ) ;
2019-09-09 16:24:27 +01:00
pointer = closureBracket + 1 ;
bSucceed = true ;
}
break ;
}
pointer + + ;
}
if ( ! bSucceed & & pointer > pExpr ) {
//The whole buffer is an comparison expression
value = evaluateComparisonExpression ( pExpr , pointer - pExpr ) ;
bSucceed = true ;
}
return bSucceed ;
}
/********************************************************************************************/
/*
* Evaluate a logical expression
* Logic expression is constructed with multiple comparison expressions and logical
* operators between them . For example : Mem1 = = 0 AND ( time > sunrise + 60 ) .
* Parenthesis are allowed to change the priority of logical operators .
* Input :
* expression - A logical expression
* len - Length of the expression
* Output :
* N / A
* Return :
* boolean - the value of logical expression
*/
bool evaluateLogicalExpression ( const char * expression , int len )
{
bool bResult = false ;
//Make a copy first
char expbuff [ len + 1 ] ;
memcpy ( expbuff , expression , len ) ;
expbuff [ len ] = ' \0 ' ;
//snprintf_P(log_data, sizeof(log_data), PSTR("EvalLogic: |%s|"), expbuff);
//AddLog(LOG_LEVEL_DEBUG);
char * pointer = expbuff ;
LinkedList < bool > values ;
LinkedList < int8_t > logicOperators ;
//Find first comparison expression
bool bValue ;
if ( findNextLogicObjectValue ( pointer , bValue ) ) {
values . add ( bValue ) ;
} else {
return false ;
}
int8_t op ;
while ( * pointer ) {
if ( findNextLogicOperator ( pointer , op )
& & ( * pointer ) & & findNextLogicObjectValue ( pointer , bValue ) )
{
logicOperators . add ( op ) ;
values . add ( bValue ) ;
} else {
break ;
}
}
//Calculate all "AND" first
int index = 0 ;
while ( index < logicOperators . size ( ) ) {
if ( logicOperators . get ( index ) = = LOGIC_OPERATOR_AND ) {
values . set ( index , values . get ( index ) & & values . get ( index + 1 ) ) ;
values . remove ( index + 1 ) ;
logicOperators . remove ( index ) ;
} else {
index + + ;
}
}
//Then, calculate all "OR"
index = 0 ;
while ( index < logicOperators . size ( ) ) {
if ( logicOperators . get ( index ) = = LOGIC_OPERATOR_OR ) {
values . set ( index , values . get ( index ) | | values . get ( index + 1 ) ) ;
values . remove ( index + 1 ) ;
logicOperators . remove ( index ) ;
} else {
index + + ;
}
}
return values . get ( 0 ) ;
}
/********************************************************************************************/
/*
* This function search in a buffer to find out an IF block start from current position
* Note : All the tokens found during the searching will be changed to NULL terminated string .
* Please make a copy before call this function if you still need it .
* Input :
* pointer - Point to a NULL end string buffer with the commands
* lenWord - Accept the length of block end word
* block_type - The block type you are looking for .
* Output :
* pointer - pointer point to the end of if block .
* lenWord - The length of block end word ( " ENDIF " , " ELSEIF " , " ELSE " )
* Return :
* The block type we find .
* IF_BLOCK_INVALID - Failed .
*/
int8_t findIfBlock ( char * & pointer , int & lenWord , int8_t block_type )
{
int8_t foundBlock = IF_BLOCK_INVALID ;
//First break into words delimited by space or ";"
const char * word ;
while ( * pointer ) {
if ( ! isalpha ( * pointer ) ) {
pointer + + ;
continue ;
}
word = pointer ;
while ( * pointer & & isalpha ( * pointer ) ) {
pointer + + ;
}
lenWord = pointer - word ;
if ( 2 = = lenWord & & 0 = = strncasecmp_P ( word , PSTR ( " IF " ) , 2 ) ) {
//if we find a new "IF" that means this is nested if block
//Try to finish this nested if block
if ( findIfBlock ( pointer , lenWord , IF_BLOCK_ENDIF ) ! = IF_BLOCK_ENDIF ) {
//If failed, we done.
break ;
}
} else if ( ( IF_BLOCK_ENDIF = = block_type | | IF_BLOCK_ANY = = block_type )
& & ( 5 = = lenWord ) & & ( 0 = = strncasecmp_P ( word , PSTR ( " ENDIF " ) , 5 ) ) )
{
//Find an "ENDIF"
foundBlock = IF_BLOCK_ENDIF ;
break ;
} else if ( ( IF_BLOCK_ELSEIF = = block_type | | IF_BLOCK_ANY = = block_type )
& & ( 6 = = lenWord ) & & ( 0 = = strncasecmp_P ( word , PSTR ( " ELSEIF " ) , 6 ) ) )
{
//Find an "ELSEIF"
foundBlock = IF_BLOCK_ELSEIF ;
break ;
} else if ( ( IF_BLOCK_ELSE = = block_type | | IF_BLOCK_ANY = = block_type )
& & ( 4 = = lenWord ) & & ( 0 = = strncasecmp_P ( word , PSTR ( " ELSE " ) , 4 ) ) )
{
//Find an "ELSE"
foundBlock = IF_BLOCK_ELSE ;
break ;
}
}
return foundBlock ;
}
/********************************************************************************************/
/*
* This function is used to execute a commands block in if statement when one of the condition is true .
* Input :
* commands - A char buffer include ( but not limited ) the commands block need to execute
* len - Length of the commands block
* Output :
N / A
* Return :
* void
*/
void ExecuteCommandBlock ( const char * commands , int len )
{
char cmdbuff [ len + 1 ] ; //apply enough space
memcpy ( cmdbuff , commands , len ) ;
cmdbuff [ len ] = ' \0 ' ;
//snprintf_P(log_data, sizeof(log_data), PSTR("ExecCmd: |%s|"), cmdbuff);
//AddLog(LOG_LEVEL_DEBUG);
char oneCommand [ len + 1 ] ; //To put one command
int insertPosition = 0 ; //When insert into backlog, we should do it by 0, 1, 2 ...
char * pos = cmdbuff ;
int lenEndBlock = 0 ;
while ( * pos ) {
if ( isspace ( * pos ) | | ' \x1e ' = = * pos | | ' ; ' = = * pos ) {
pos + + ;
continue ;
}
if ( strncasecmp_P ( pos , PSTR ( " BACKLOG " ) , 8 ) = = 0 ) {
//Skip "BACKLOG " and set not first command flag. So all followed command will be send to backlog
pos + = 8 ;
continue ;
}
if ( strncasecmp_P ( pos , PSTR ( " IF " ) , 3 ) = = 0 ) {
//Has a nested IF statement
//Find the matched ENDIF
char * pEndif = pos + 3 ; //Skip "IF "
if ( IF_BLOCK_ENDIF ! = findIfBlock ( pEndif , lenEndBlock , IF_BLOCK_ENDIF ) ) {
//Cannot find matched endif, stop execution.
break ;
}
//We has the whole IF statement, copy to oneCommand
memcpy ( oneCommand , pos , pEndif - pos ) ;
oneCommand [ pEndif - pos ] = ' \0 ' ;
pos = pEndif ;
} else { //Normal command
//Looking for the command end single - '\x1e'
char * pEndOfCommand = strpbrk ( pos , " \x1e ; " ) ;
if ( NULL = = pEndOfCommand ) {
pEndOfCommand = pos + strlen ( pos ) ;
}
memcpy ( oneCommand , pos , pEndOfCommand - pos ) ;
oneCommand [ pEndOfCommand - pos ] = ' \0 ' ;
pos = pEndOfCommand ;
}
//Start to process current command we found
//Going to insert the command into backlog
String sCurrentCommand = oneCommand ;
sCurrentCommand . trim ( ) ;
if ( sCurrentCommand . length ( ) > 0
& & backlog . size ( ) < MAX_BACKLOG & & ! backlog_mutex )
{
//Insert into backlog
backlog_mutex = true ;
backlog . add ( insertPosition , sCurrentCommand ) ;
backlog_mutex = false ;
insertPosition + + ;
}
}
return ;
}
/********************************************************************************************/
/*
* Execute IF statement . This is the place to run a " IF ... " command .
* Input :
* statements - The IF statement we are going to process
* Output :
N / A
* Return :
* void
*/
void ProcessIfStatement ( const char * statements )
{
String conditionExpression ;
int len = strlen ( statements ) ;
char statbuff [ len + 1 ] ;
memcpy ( statbuff , statements , len + 1 ) ;
char * pos = statbuff ;
int lenEndBlock = 0 ;
while ( true ) { //Each loop process one IF (or ELSEIF) block
//Find and test the condition expression followed the IF or ELSEIF
//Search for the open bracket first
while ( * pos & & * pos ! = ' ( ' ) {
pos + + ;
}
2019-09-10 10:05:53 +01:00
if ( 0 = = * pos ) { break ; }
2019-09-09 16:24:27 +01:00
char * posEnd = findClosureBracket ( pos ) ;
if ( true = = evaluateLogicalExpression ( pos + 1 , posEnd - ( pos + 1 ) ) ) {
//Looking for matched "ELSEIF", "ELSE" or "ENDIF", then Execute this block
char * cmdBlockStart = posEnd + 1 ;
char * cmdBlockEnd = cmdBlockStart ;
int8_t nextBlock = findIfBlock ( cmdBlockEnd , lenEndBlock , IF_BLOCK_ANY ) ;
if ( IF_BLOCK_INVALID = = nextBlock ) {
//Failed
break ;
}
ExecuteCommandBlock ( cmdBlockStart , cmdBlockEnd - cmdBlockStart - lenEndBlock ) ;
pos = cmdBlockEnd ;
break ;
} else { //Does not match the IF condition, going to check elseif and else
pos = posEnd + 1 ;
int8_t nextBlock = findIfBlock ( pos , lenEndBlock , IF_BLOCK_ANY ) ;
if ( IF_BLOCK_ELSEIF = = nextBlock ) {
//Continue process next ELSEIF block like IF
continue ;
} else if ( IF_BLOCK_ELSE = = nextBlock ) {
//Looking for matched "ENDIF" then execute this block
char * cmdBlockEnd = pos ;
int8_t nextBlock = findIfBlock ( cmdBlockEnd , lenEndBlock , IF_BLOCK_ENDIF ) ;
if ( IF_BLOCK_ENDIF ! = nextBlock ) {
//Failed
break ;
}
ExecuteCommandBlock ( pos , cmdBlockEnd - pos - lenEndBlock ) ;
break ;
} else { // IF_BLOCK_ENDIF == nextBlock
//We done
break ;
}
}
}
}
/********************************************************************************************/
/*
* This function is called in Rules event handler to process any command between DO . . . ENDON ( BREAK )
* - Do escape ( convert " ; " into " \x1e " ) for all IF statements .
* Input :
* commands - The commands block need to execute
* Output :
N / A
* Return :
* void
*/
void RulesPreprocessCommand ( char * pCommands )
{
char * cmd = pCommands ;
int lenEndBlock = 0 ;
while ( * cmd ) {
//Skip all ";" and space between two commands
if ( ' ; ' = = * cmd | | isspace ( * cmd ) ) {
cmd + + ;
}
else if ( strncasecmp_P ( cmd , PSTR ( " IF " ) , 3 ) = = 0 ) { //found IF block
//We are going to look for matched "ENDIF"
char * pIfStart = cmd ;
char * pIfEnd = pIfStart + 3 ; //Skip "IF "
//int pIfStart = cmd - command; //"IF" statement block start at position (relative to command start)
if ( IF_BLOCK_ENDIF = = findIfBlock ( pIfEnd , lenEndBlock , IF_BLOCK_ENDIF ) ) {
//Found the ENDIF
cmd = pIfEnd ; //Will continue process from here
//Escapte from ";" to "\x1e".
//By remove all ";" in IF statement block, we can prevent backlog command cut the whole block as multiple commands
while ( pIfStart < pIfEnd ) {
if ( ' ; ' = = * pIfStart )
* pIfStart = ' \x1e ' ;
pIfStart + + ;
}
}
else { //Did not find the matched ENDIF, stop processing
break ;
}
}
else { //Other commands, skip it
while ( * cmd & & ' ; ' ! = * cmd ) {
cmd + + ;
}
}
}
return ;
}
# endif //SUPPORT_IF_STATEMENT
2019-08-02 15:23:17 +01:00
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
2019-08-02 15:23:17 +01:00
void CmndRule ( void )
2018-04-13 16:42:11 +01:00
{
uint8_t index = XdrvMailbox . index ;
2019-08-02 15:23:17 +01:00
if ( ( index > 0 ) & & ( index < = MAX_RULE_SETS ) ) {
2018-05-24 13:25:52 +01:00
if ( ( XdrvMailbox . data_len > 0 ) & & ( XdrvMailbox . data_len < sizeof ( Settings . rules [ index - 1 ] ) ) ) {
2018-08-28 09:26:33 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 10 ) ) {
2018-04-13 16:42:11 +01:00
switch ( XdrvMailbox . payload ) {
case 0 : // Off
case 1 : // On
2018-05-24 13:25:52 +01:00
bitWrite ( Settings . rule_enabled , index - 1 , XdrvMailbox . payload ) ;
2018-04-13 16:42:11 +01:00
break ;
case 2 : // Toggle
2018-05-24 13:25:52 +01:00
bitWrite ( Settings . rule_enabled , index - 1 , bitRead ( Settings . rule_enabled , index - 1 ) ^ 1 ) ;
2018-04-13 16:42:11 +01:00
break ;
case 4 : // Off
case 5 : // On
2018-05-24 13:25:52 +01:00
bitWrite ( Settings . rule_once , index - 1 , XdrvMailbox . payload & 1 ) ;
2018-04-13 16:42:11 +01:00
break ;
case 6 : // Toggle
2018-05-24 13:25:52 +01:00
bitWrite ( Settings . rule_once , index - 1 , bitRead ( Settings . rule_once , index - 1 ) ^ 1 ) ;
2018-04-13 16:42:11 +01:00
break ;
2018-08-28 09:26:33 +01:00
case 8 : // Off
case 9 : // On
bitWrite ( Settings . rule_stop , index - 1 , XdrvMailbox . payload & 1 ) ;
break ;
case 10 : // Toggle
bitWrite ( Settings . rule_stop , index - 1 , bitRead ( Settings . rule_stop , index - 1 ) ^ 1 ) ;
break ;
2018-04-13 16:42:11 +01:00
}
} else {
2018-07-29 13:45:42 +01:00
int offset = 0 ;
if ( ' + ' = = XdrvMailbox . data [ 0 ] ) {
offset = strlen ( Settings . rules [ index - 1 ] ) ;
if ( XdrvMailbox . data_len < ( sizeof ( Settings . rules [ index - 1 ] ) - offset - 1 ) ) { // Check free space
XdrvMailbox . data [ 0 ] = ' ' ; // Remove + and make sure at least one space is inserted
} else {
offset = - 1 ; // Not enough space so skip it
}
}
if ( offset ! = - 1 ) {
strlcpy ( Settings . rules [ index - 1 ] + offset , ( ' " ' = = XdrvMailbox . data [ 0 ] ) ? " " : XdrvMailbox . data , sizeof ( Settings . rules [ index - 1 ] ) ) ;
}
2018-04-13 16:42:11 +01:00
}
2019-08-17 13:27:41 +01:00
Rules . triggers [ index - 1 ] = 0 ; // Reset once flag
2018-04-13 16:42:11 +01:00
}
2018-08-28 09:26:33 +01:00
snprintf_P ( mqtt_data , sizeof ( mqtt_data ) , PSTR ( " { \" %s%d \" : \" %s \" , \" Once \" : \" %s \" , \" StopOnError \" : \" %s \" , \" Free \" :%d, \" Rules \" : \" %s \" } " ) ,
2019-08-02 15:23:17 +01:00
XdrvMailbox . command , index , GetStateText ( bitRead ( Settings . rule_enabled , index - 1 ) ) , GetStateText ( bitRead ( Settings . rule_once , index - 1 ) ) ,
2018-08-28 09:26:33 +01:00
GetStateText ( bitRead ( Settings . rule_stop , index - 1 ) ) , sizeof ( Settings . rules [ index - 1 ] ) - strlen ( Settings . rules [ index - 1 ] ) - 1 , Settings . rules [ index - 1 ] ) ;
2018-04-13 16:42:11 +01:00
}
2019-08-02 15:23:17 +01:00
}
void CmndRuleTimer ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_RULE_TIMERS ) ) {
2018-04-13 16:42:11 +01:00
if ( XdrvMailbox . data_len > 0 ) {
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# ifdef USE_EXPRESSION
2019-07-01 17:20:43 +01:00
float timer_set = evaluateExpression ( XdrvMailbox . data , XdrvMailbox . data_len ) ;
2019-08-17 13:27:41 +01:00
Rules . timer [ XdrvMailbox . index - 1 ] = ( timer_set > 0 ) ? millis ( ) + ( 1000 * timer_set ) : 0 ;
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# else
2019-08-17 13:27:41 +01:00
Rules . timer [ XdrvMailbox . index - 1 ] = ( XdrvMailbox . payload > 0 ) ? millis ( ) + ( 1000 * XdrvMailbox . payload ) : 0 ;
2019-08-02 15:23:17 +01:00
# endif // USE_EXPRESSION
2018-04-13 16:42:11 +01:00
}
2018-08-24 17:22:04 +01:00
mqtt_data [ 0 ] = ' \0 ' ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < MAX_RULE_TIMERS ; i + + ) {
2019-08-17 13:27:41 +01:00
ResponseAppend_P ( PSTR ( " %c \" T%d \" :%d " ) , ( i ) ? ' , ' : ' { ' , i + 1 , ( Rules . timer [ i ] ) ? ( Rules . timer [ i ] - millis ( ) ) / 1000 : 0 ) ;
2018-08-24 17:22:04 +01:00
}
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
ResponseJsonEnd ( ) ;
2018-04-13 16:42:11 +01:00
}
2019-08-02 15:23:17 +01:00
}
void CmndEvent ( void )
{
if ( XdrvMailbox . data_len > 0 ) {
2019-08-17 13:27:41 +01:00
strlcpy ( Rules . event_data , XdrvMailbox . data , sizeof ( Rules . event_data ) ) ;
2018-04-13 16:42:11 +01:00
}
2019-08-03 12:01:34 +01:00
ResponseCmndDone ( ) ;
2019-08-02 15:23:17 +01:00
}
void CmndVariable ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_RULE_VARS ) ) {
2019-07-24 10:10:15 +01:00
if ( ! XdrvMailbox . usridx ) {
mqtt_data [ 0 ] = ' \0 ' ;
for ( uint32_t i = 0 ; i < MAX_RULE_VARS ; i + + ) {
2019-08-17 13:27:41 +01:00
ResponseAppend_P ( PSTR ( " %c \" Var%d \" : \" %s \" " ) , ( i ) ? ' , ' : ' { ' , i + 1 , rules_vars [ i ] ) ;
2019-07-24 10:10:15 +01:00
}
ResponseJsonEnd ( ) ;
} else {
if ( XdrvMailbox . data_len > 0 ) {
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# ifdef USE_EXPRESSION
2019-08-30 16:07:56 +01:00
if ( XdrvMailbox . data [ 0 ] = = ' = ' ) { // Spaces already been skipped in data
dtostrfd ( evaluateExpression ( XdrvMailbox . data + 1 , XdrvMailbox . data_len - 1 ) , Settings . flag2 . calc_resolution , rules_vars [ XdrvMailbox . index - 1 ] ) ;
} else {
strlcpy ( rules_vars [ XdrvMailbox . index - 1 ] , ( ' " ' = = XdrvMailbox . data [ 0 ] ) ? " " : XdrvMailbox . data , sizeof ( rules_vars [ XdrvMailbox . index - 1 ] ) ) ;
}
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# else
2019-08-17 13:27:41 +01:00
strlcpy ( rules_vars [ XdrvMailbox . index - 1 ] , ( ' " ' = = XdrvMailbox . data [ 0 ] ) ? " " : XdrvMailbox . data , sizeof ( rules_vars [ XdrvMailbox . index - 1 ] ) ) ;
2019-08-02 15:23:17 +01:00
# endif // USE_EXPRESSION
2019-08-17 13:27:41 +01:00
bitSet ( Rules . vars_event , XdrvMailbox . index - 1 ) ;
2019-07-24 10:10:15 +01:00
}
2019-08-17 13:27:41 +01:00
ResponseCmndIdxChar ( rules_vars [ XdrvMailbox . index - 1 ] ) ;
2018-05-20 16:46:00 +01:00
}
}
2019-08-02 15:23:17 +01:00
}
void CmndMemory ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_RULE_MEMS ) ) {
2019-07-24 10:10:15 +01:00
if ( ! XdrvMailbox . usridx ) {
mqtt_data [ 0 ] = ' \0 ' ;
for ( uint32_t i = 0 ; i < MAX_RULE_MEMS ; i + + ) {
ResponseAppend_P ( PSTR ( " %c \" Mem%d \" : \" %s \" " ) , ( i ) ? ' , ' : ' { ' , i + 1 , Settings . mems [ i ] ) ;
}
ResponseJsonEnd ( ) ;
} else {
if ( XdrvMailbox . data_len > 0 ) {
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# ifdef USE_EXPRESSION
2019-08-30 16:07:56 +01:00
if ( XdrvMailbox . data [ 0 ] = = ' = ' ) { // Spaces already been skipped in data
dtostrfd ( evaluateExpression ( XdrvMailbox . data + 1 , XdrvMailbox . data_len - 1 ) , Settings . flag2 . calc_resolution , Settings . mems [ XdrvMailbox . index - 1 ] ) ;
} else {
strlcpy ( Settings . mems [ XdrvMailbox . index - 1 ] , ( ' " ' = = XdrvMailbox . data [ 0 ] ) ? " " : XdrvMailbox . data , sizeof ( Settings . mems [ XdrvMailbox . index - 1 ] ) ) ;
}
Introduce Expression in Rules
Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset), operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^ (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
2019-02-13 02:46:42 +00:00
# else
2019-08-02 15:23:17 +01:00
strlcpy ( Settings . mems [ XdrvMailbox . index - 1 ] , ( ' " ' = = XdrvMailbox . data [ 0 ] ) ? " " : XdrvMailbox . data , sizeof ( Settings . mems [ XdrvMailbox . index - 1 ] ) ) ;
# endif // USE_EXPRESSION
2019-08-17 13:27:41 +01:00
bitSet ( Rules . mems_event , XdrvMailbox . index - 1 ) ;
2019-07-24 10:10:15 +01:00
}
2019-08-03 12:01:34 +01:00
ResponseCmndIdxChar ( Settings . mems [ XdrvMailbox . index - 1 ] ) ;
2018-05-20 16:46:00 +01:00
}
}
2019-08-02 15:23:17 +01:00
}
void CmndCalcResolution ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 7 ) ) {
Settings . flag2 . calc_resolution = XdrvMailbox . payload ;
2018-11-27 00:22:44 +00:00
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber ( Settings . flag2 . calc_resolution ) ;
2019-08-02 15:23:17 +01:00
}
void CmndAddition ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_RULE_VARS ) ) {
2018-07-16 11:37:49 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2019-08-17 13:27:41 +01:00
float tempvar = CharToFloat ( rules_vars [ XdrvMailbox . index - 1 ] ) + CharToFloat ( XdrvMailbox . data ) ;
dtostrfd ( tempvar , Settings . flag2 . calc_resolution , rules_vars [ XdrvMailbox . index - 1 ] ) ;
bitSet ( Rules . vars_event , XdrvMailbox . index - 1 ) ;
2018-06-10 06:09:11 +01:00
}
2019-08-17 13:27:41 +01:00
ResponseCmndIdxChar ( rules_vars [ XdrvMailbox . index - 1 ] ) ;
2018-06-10 06:09:11 +01:00
}
2019-08-02 15:23:17 +01:00
}
void CmndSubtract ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_RULE_VARS ) ) {
2018-07-16 11:37:49 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2019-08-17 13:27:41 +01:00
float tempvar = CharToFloat ( rules_vars [ XdrvMailbox . index - 1 ] ) - CharToFloat ( XdrvMailbox . data ) ;
dtostrfd ( tempvar , Settings . flag2 . calc_resolution , rules_vars [ XdrvMailbox . index - 1 ] ) ;
bitSet ( Rules . vars_event , XdrvMailbox . index - 1 ) ;
2018-06-10 06:09:11 +01:00
}
2019-08-17 13:27:41 +01:00
ResponseCmndIdxChar ( rules_vars [ XdrvMailbox . index - 1 ] ) ;
2018-06-10 06:09:11 +01:00
}
2019-08-02 15:23:17 +01:00
}
void CmndMultiply ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_RULE_VARS ) ) {
2018-07-16 11:37:49 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2019-08-17 13:27:41 +01:00
float tempvar = CharToFloat ( rules_vars [ XdrvMailbox . index - 1 ] ) * CharToFloat ( XdrvMailbox . data ) ;
dtostrfd ( tempvar , Settings . flag2 . calc_resolution , rules_vars [ XdrvMailbox . index - 1 ] ) ;
bitSet ( Rules . vars_event , XdrvMailbox . index - 1 ) ;
2018-06-10 06:09:11 +01:00
}
2019-08-17 13:27:41 +01:00
ResponseCmndIdxChar ( rules_vars [ XdrvMailbox . index - 1 ] ) ;
2018-06-10 06:09:11 +01:00
}
2019-08-02 15:23:17 +01:00
}
void CmndScale ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_RULE_VARS ) ) {
2018-07-16 11:37:49 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2019-03-26 17:26:50 +00:00
if ( strstr ( XdrvMailbox . data , " , " ) ! = nullptr ) { // Process parameter entry
2018-07-16 11:37:49 +01:00
char sub_string [ XdrvMailbox . data_len + 1 ] ;
2019-07-01 17:20:43 +01:00
float valueIN = CharToFloat ( subStr ( sub_string , XdrvMailbox . data , " , " , 1 ) ) ;
float fromLow = CharToFloat ( subStr ( sub_string , XdrvMailbox . data , " , " , 2 ) ) ;
float fromHigh = CharToFloat ( subStr ( sub_string , XdrvMailbox . data , " , " , 3 ) ) ;
float toLow = CharToFloat ( subStr ( sub_string , XdrvMailbox . data , " , " , 4 ) ) ;
float toHigh = CharToFloat ( subStr ( sub_string , XdrvMailbox . data , " , " , 5 ) ) ;
float value = map_double ( valueIN , fromLow , fromHigh , toLow , toHigh ) ;
2019-08-17 13:27:41 +01:00
dtostrfd ( value , Settings . flag2 . calc_resolution , rules_vars [ XdrvMailbox . index - 1 ] ) ;
bitSet ( Rules . vars_event , XdrvMailbox . index - 1 ) ;
2018-06-10 06:09:11 +01:00
}
}
2019-08-17 13:27:41 +01:00
ResponseCmndIdxChar ( rules_vars [ XdrvMailbox . index - 1 ] ) ;
2018-06-10 06:09:11 +01:00
}
2018-04-13 16:42:11 +01:00
}
2019-07-01 17:20:43 +01:00
float map_double ( float x , float in_min , float in_max , float out_min , float out_max )
2018-06-28 17:06:21 +01:00
{
2019-08-02 15:23:17 +01:00
return ( x - in_min ) * ( out_max - out_min ) / ( in_max - in_min ) + out_min ;
2018-06-28 17:06:21 +01:00
}
2018-04-13 16:42:11 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-01-28 13:08:33 +00:00
bool Xdrv10 ( uint8_t function )
2018-04-13 16:42:11 +01:00
{
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-04-13 16:42:11 +01:00
switch ( function ) {
case FUNC_EVERY_50_MSECOND :
RulesEvery50ms ( ) ;
break ;
2018-08-26 16:10:18 +01:00
case FUNC_EVERY_100_MSECOND :
RulesEvery100ms ( ) ;
break ;
2018-04-13 16:42:11 +01:00
case FUNC_EVERY_SECOND :
RulesEverySecond ( ) ;
break ;
2018-05-06 15:07:42 +01:00
case FUNC_SET_POWER :
RulesSetPower ( ) ;
break ;
2018-04-13 16:42:11 +01:00
case FUNC_COMMAND :
2019-08-02 15:23:17 +01:00
result = DecodeCommand ( kRulesCommands , RulesCommand ) ;
2018-04-13 16:42:11 +01:00
break ;
2018-05-24 15:23:20 +01:00
case FUNC_RULES_PROCESS :
result = RulesProcess ( ) ;
break ;
2019-05-19 11:42:10 +01:00
case FUNC_SAVE_BEFORE_RESTART :
RulesSaveBeforeRestart ( ) ;
break ;
Rules: Trigger Event with MQTT Subscriptions
Support subscribe/unsubscribe MQTT topics and trigger specified event with the subscribed MQTT topic.
You can subscribe a MQTT topic and assign an event name. Once we received subscribed MQTT message, an event will be automatically triggered. So you can set up a rule with "ON EVENT#<event_name> DO ..." to do whatever you want based on this MQTT message. The payload is passed as a parameter once the event been triggered. If the payload is in JSON format, you are able to get the value of specified key as parameter.
For example, if you have a Tasmota based thermostat and multiple temperature sensors in different place, usually you have to set up a centre home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.
Two new commands in Rules:
1. Subscribe
Subscribe a MQTT topic (with or without key) and assign an event name to it.
Command format:
Subscribe [<event_name>, <topic> [, <key>]]
This command will subscribe a <topic> and give it an event name <event_name>.
The optional parameter <key> is for parse the specified key/value from MQTT message
payload with JSON format.
In order to parse value from two level JSON data, you can use one dot (".") to split the key into two section.
Subscribe command without any parameter will list all topics currently subscribed.
2. Unsubscribe
Unsubscribe specified MQTT event.
Command format:
Unsubscribe [<event_name>]
Unsubscribe a topic subscribed by specify the event name.
If no event specified, Unsubscribe all topics subscribed.
Examples:
1.
Subscribe BkLight, Tasmota/BackyardLight/stat/POWER
And define a rule like:
Rule1 on event#BkLight=ON do ruletimer4 60 endon
2.
Subscribe DnTemp, Tasmota/RoomSensor1/stat/SENSOR, DS18B20.Temperature
Define a rule to deal with the MQTT message like {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
Rule1 ON EVENT#DnTemp>=21 DO ... ENDON
2019-02-24 03:33:09 +00:00
# ifdef SUPPORT_MQTT_EVENT
case FUNC_MQTT_DATA :
result = RulesMqttData ( ) ;
break ;
2019-08-02 15:23:17 +01:00
# endif // SUPPORT_MQTT_EVENT
2019-09-29 17:00:01 +01:00
case FUNC_PRE_INIT :
RulesInit ( ) ;
break ;
2018-04-13 16:42:11 +01:00
}
return result ;
}
2019-05-18 12:34:52 +01:00
# endif // Do not USE_SCRIPT
2019-07-01 17:20:43 +01:00
# endif // USE_RULES