mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-23 18:56:38 +00:00
Berry 'tasmota.add_rule_once' and auto-remove rules with same pattern and id (#22900)
This commit is contained in:
parent
9bb7b7913a
commit
0dcd38186f
@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file.
|
||||
- LVLG/HASPmota add color names from OpenHASP (#22879)
|
||||
- HASPmota support for `buttonmatrix` events
|
||||
- Berry driver for PN532 NFC/Mifare reader
|
||||
- Berry `tasmota.add_rule_once` and auto-remove rules with same pattern and id
|
||||
|
||||
### Breaking Changed
|
||||
|
||||
|
@ -167,6 +167,7 @@ class be_class_tasmota (scope: global, name: Tasmota) {
|
||||
find_list_i, closure(class_Tasmota_find_list_i_closure)
|
||||
find_op, closure(class_Tasmota_find_op_closure)
|
||||
add_rule, closure(class_Tasmota_add_rule_closure)
|
||||
add_rule_once, closure(class_Tasmota_add_rule_once_closure)
|
||||
remove_rule, closure(class_Tasmota_remove_rule_closure)
|
||||
try_rule, closure(class_Tasmota_try_rule_closure)
|
||||
exec_rules, closure(class_Tasmota_exec_rules_closure)
|
||||
|
@ -90,13 +90,31 @@ class Tasmota
|
||||
end
|
||||
|
||||
# Rules
|
||||
def add_rule(pat, f, id)
|
||||
def add_rule_once(pat, f, id)
|
||||
self.add_rule(pat, f, id, true)
|
||||
end
|
||||
# add_rule(pat, f, id, run_once)
|
||||
#
|
||||
# pat: (string) pattern for the rule
|
||||
# f: (function) the function to be called when the rule fires
|
||||
# there is a check that the caller doesn't use mistakenly
|
||||
# a method - in such case a closure needs to be used instead
|
||||
# id: (opt, any) an optional id so the rule can be removed later
|
||||
# needs to be unique to avoid collision
|
||||
# No test for uniqueness is performed
|
||||
# run_once: (opt, bool or nil) indicates the rule is fired only once
|
||||
# this parameter is not used directly but instead
|
||||
# set by 'add_rule_once()'
|
||||
def add_rule(pat, f, id, run_once)
|
||||
self.check_not_method(f)
|
||||
if self._rules == nil
|
||||
self._rules = []
|
||||
end
|
||||
if type(f) == 'function'
|
||||
self._rules.push(Trigger(self.Rule_Matcher.parse(pat), f, id))
|
||||
if (id != nil)
|
||||
self.remove_rule(pat, id)
|
||||
end
|
||||
self._rules.push(Trigger(self.Rule_Matcher.parse(pat), f, id, run_once))
|
||||
else
|
||||
raise 'value_error', 'the second argument is not a function'
|
||||
end
|
||||
@ -115,6 +133,59 @@ class Tasmota
|
||||
end
|
||||
end
|
||||
|
||||
#-
|
||||
# Below is a unit test for add_rule and add_rule_once
|
||||
var G1, G2, G3
|
||||
def f1() print("F1") G1 = 1 return true end
|
||||
def f2() print("F2") G2 = 2 return true end
|
||||
def f3() print("F3") G3 = 3 return true end
|
||||
|
||||
|
||||
tasmota.add_rule("A#B", f1, "f1")
|
||||
tasmota.add_rule_once("A#B", f2, "f2")
|
||||
|
||||
var r
|
||||
r = tasmota.publish_rule('{"A":{"B":1}}')
|
||||
|
||||
assert(G1 == 1)
|
||||
assert(G2 == 2)
|
||||
assert(G3 == nil)
|
||||
#assert(r == true)
|
||||
|
||||
G1 = nil
|
||||
G2 = nil
|
||||
|
||||
r = tasmota.publish_rule('{"A":{"B":1}}')
|
||||
|
||||
assert(G1 == 1)
|
||||
assert(G2 == nil)
|
||||
assert(G3 == nil)
|
||||
#assert(r == true)
|
||||
|
||||
tasmota.add_rule("A#B", f3, "f1")
|
||||
|
||||
G1 = nil
|
||||
|
||||
r = tasmota.publish_rule('{"A":{"B":1}}')
|
||||
|
||||
assert(G1 == nil)
|
||||
assert(G2 == nil)
|
||||
assert(G3 == 3)
|
||||
#assert(r == true)
|
||||
|
||||
tasmota.remove_rule("A#B", "f1")
|
||||
|
||||
G3 = nil
|
||||
|
||||
r = tasmota.publish_rule('{"A":{"B":1}}')
|
||||
|
||||
assert(G1 == nil)
|
||||
assert(G2 == nil)
|
||||
assert(G3 == nil)
|
||||
#assert(r == false)
|
||||
|
||||
-#
|
||||
|
||||
# Rules trigger if match. return true if match, false if not
|
||||
#
|
||||
# event: native Berry map representing the JSON input
|
||||
@ -133,27 +204,35 @@ class Tasmota
|
||||
|
||||
# Run rules, i.e. check each individual rule
|
||||
# Returns true if at least one rule matched, false if none
|
||||
# `exec_rule` is true, then run the rule, else just record value
|
||||
#
|
||||
# ev_json: (string) the payload of the rule, needs to be JSON format
|
||||
# exec_rule: (bool) 'true' run the rule, 'false' just record value (avoind infinite loops)
|
||||
def exec_rules(ev_json, exec_rule)
|
||||
var save_cmd_res = self.cmd_res # save initial state (for reentrance)
|
||||
if self._rules || save_cmd_res != nil # if there is a rule handler, or we record rule results
|
||||
var save_cmd_res = self.cmd_res # save initial state (for reentrance)
|
||||
if self._rules || save_cmd_res != nil # if there is a rule handler, or we record rule results
|
||||
import json
|
||||
|
||||
self.cmd_res = nil # disable sunsequent recording of results
|
||||
var ret = false
|
||||
var ret = false # ret records if any rule was fired
|
||||
|
||||
var ev = json.load(ev_json) # returns nil if invalid JSON
|
||||
if ev == nil
|
||||
self.log('BRY: ERROR, bad json: '+ev_json, 3)
|
||||
ev = ev_json # revert to string
|
||||
self.log('BRY: ERROR, bad json: ' + ev_json, 3)
|
||||
ev = ev_json # revert to string
|
||||
end
|
||||
# try all rule handlers
|
||||
if exec_rule && self._rules
|
||||
var i = 0
|
||||
while i < size(self._rules)
|
||||
var tr = self._rules[i]
|
||||
ret = self.try_rule(ev,tr.trig,tr.f) || ret #- call should be first to avoid evaluation shortcut if ret is already true -#
|
||||
i += 1
|
||||
var rule_fired = self.try_rule(ev, tr.trig, tr.f)
|
||||
ret = ret || rule_fired # 'or' with result
|
||||
if rule_fired && (tr.o == true)
|
||||
# this rule should be run_once(d) so remove it
|
||||
self._rules.remove(i)
|
||||
else
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
class Trigger
|
||||
var trig, f, id
|
||||
var o # optional object
|
||||
var o # optional object, for Cron it contains the cron object, for rule, 'true' means run-once
|
||||
|
||||
# trig: trigger of the event, either timestamp (int) or a rule matcher instance
|
||||
# f: function or closure to call
|
||||
@ -24,6 +24,10 @@ class Trigger
|
||||
str(self.trig), str(self.f), str(self.id))
|
||||
end
|
||||
|
||||
def run_once()
|
||||
return self.o == true
|
||||
end
|
||||
|
||||
###########################################################################################
|
||||
# For cron triggers only
|
||||
###########################################################################################
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,21 +3,113 @@
|
||||
* Generated code, don't edit *
|
||||
\********************************************************************/
|
||||
#include "be_constobj.h"
|
||||
// compact class 'Trigger' ktab size: 8, total: 14 (saved 48 bytes)
|
||||
// compact class 'Trigger' ktab size: 8, total: 15 (saved 56 bytes)
|
||||
static const bvalue be_ktab_class_Trigger[8] = {
|
||||
/* K0 */ be_nested_str(trig),
|
||||
/* K1 */ be_nested_str(f),
|
||||
/* K2 */ be_nested_str(id),
|
||||
/* K3 */ be_nested_str(o),
|
||||
/* K4 */ be_nested_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29),
|
||||
/* K5 */ be_const_int(0),
|
||||
/* K6 */ be_nested_str(time_reached),
|
||||
/* K7 */ be_nested_str(next),
|
||||
/* K0 */ be_nested_str(o),
|
||||
/* K1 */ be_nested_str(next),
|
||||
/* K2 */ be_nested_str(trig),
|
||||
/* K3 */ be_const_int(0),
|
||||
/* K4 */ be_nested_str(time_reached),
|
||||
/* K5 */ be_nested_str(f),
|
||||
/* K6 */ be_nested_str(id),
|
||||
/* K7 */ be_nested_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29),
|
||||
};
|
||||
|
||||
|
||||
extern const bclass be_class_Trigger;
|
||||
|
||||
/********************************************************************
|
||||
** Solidified function: run_once
|
||||
********************************************************************/
|
||||
be_local_closure(class_Trigger_run_once, /* name */
|
||||
be_nested_proto(
|
||||
3, /* nstack */
|
||||
1, /* argc */
|
||||
10, /* varg */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
&be_ktab_class_Trigger, /* shared constants */
|
||||
&be_const_str_run_once,
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[ 4]) { /* code */
|
||||
0x88040100, // 0000 GETMBR R1 R0 K0
|
||||
0x50080200, // 0001 LDBOOL R2 1 0
|
||||
0x1C040202, // 0002 EQ R1 R1 R2
|
||||
0x80040200, // 0003 RET 1 R1
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
** Solidified function: next
|
||||
********************************************************************/
|
||||
be_local_closure(class_Trigger_next, /* name */
|
||||
be_nested_proto(
|
||||
3, /* nstack */
|
||||
1, /* argc */
|
||||
10, /* varg */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
&be_ktab_class_Trigger, /* shared constants */
|
||||
&be_const_str_next,
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[ 7]) { /* code */
|
||||
0x88040100, // 0000 GETMBR R1 R0 K0
|
||||
0x78060003, // 0001 JMPF R1 #0006
|
||||
0x88040100, // 0002 GETMBR R1 R0 K0
|
||||
0x8C040301, // 0003 GETMET R1 R1 K1
|
||||
0x7C040200, // 0004 CALL R1 1
|
||||
0x80040200, // 0005 RET 1 R1
|
||||
0x80000000, // 0006 RET 0
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
** Solidified function: time_reached
|
||||
********************************************************************/
|
||||
be_local_closure(class_Trigger_time_reached, /* name */
|
||||
be_nested_proto(
|
||||
4, /* nstack */
|
||||
1, /* argc */
|
||||
10, /* varg */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
&be_ktab_class_Trigger, /* shared constants */
|
||||
&be_const_str_time_reached,
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[12]) { /* code */
|
||||
0x88040100, // 0000 GETMBR R1 R0 K0
|
||||
0x78060007, // 0001 JMPF R1 #000A
|
||||
0x88040102, // 0002 GETMBR R1 R0 K2
|
||||
0x24040303, // 0003 GT R1 R1 K3
|
||||
0x78060004, // 0004 JMPF R1 #000A
|
||||
0x88040100, // 0005 GETMBR R1 R0 K0
|
||||
0x8C040304, // 0006 GETMET R1 R1 K4
|
||||
0x880C0102, // 0007 GETMBR R3 R0 K2
|
||||
0x7C040400, // 0008 CALL R1 2
|
||||
0x80040200, // 0009 RET 1 R1
|
||||
0x50040000, // 000A LDBOOL R1 0 0
|
||||
0x80040200, // 000B RET 1 R1
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
** Solidified function: init
|
||||
********************************************************************/
|
||||
@ -35,10 +127,10 @@ be_local_closure(class_Trigger_init, /* name */
|
||||
&be_const_str_init,
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[ 5]) { /* code */
|
||||
0x90020001, // 0000 SETMBR R0 K0 R1
|
||||
0x90020202, // 0001 SETMBR R0 K1 R2
|
||||
0x90020403, // 0002 SETMBR R0 K2 R3
|
||||
0x90020604, // 0003 SETMBR R0 K3 R4
|
||||
0x90020401, // 0000 SETMBR R0 K2 R1
|
||||
0x90020A02, // 0001 SETMBR R0 K5 R2
|
||||
0x90020C03, // 0002 SETMBR R0 K6 R3
|
||||
0x90020004, // 0003 SETMBR R0 K0 R4
|
||||
0x80000000, // 0004 RET 0
|
||||
})
|
||||
)
|
||||
@ -64,20 +156,20 @@ be_local_closure(class_Trigger_tostring, /* name */
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[18]) { /* code */
|
||||
0x60040018, // 0000 GETGBL R1 G24
|
||||
0x58080004, // 0001 LDCONST R2 K4
|
||||
0x58080007, // 0001 LDCONST R2 K7
|
||||
0x600C0008, // 0002 GETGBL R3 G8
|
||||
0x60100006, // 0003 GETGBL R4 G6
|
||||
0x5C140000, // 0004 MOVE R5 R0
|
||||
0x7C100200, // 0005 CALL R4 1
|
||||
0x7C0C0200, // 0006 CALL R3 1
|
||||
0x60100008, // 0007 GETGBL R4 G8
|
||||
0x88140100, // 0008 GETMBR R5 R0 K0
|
||||
0x88140102, // 0008 GETMBR R5 R0 K2
|
||||
0x7C100200, // 0009 CALL R4 1
|
||||
0x60140008, // 000A GETGBL R5 G8
|
||||
0x88180101, // 000B GETMBR R6 R0 K1
|
||||
0x88180105, // 000B GETMBR R6 R0 K5
|
||||
0x7C140200, // 000C CALL R5 1
|
||||
0x60180008, // 000D GETGBL R6 G8
|
||||
0x881C0102, // 000E GETMBR R7 R0 K2
|
||||
0x881C0106, // 000E GETMBR R7 R0 K6
|
||||
0x7C180200, // 000F CALL R6 1
|
||||
0x7C040A00, // 0010 CALL R1 5
|
||||
0x80040200, // 0011 RET 1 R1
|
||||
@ -87,87 +179,23 @@ be_local_closure(class_Trigger_tostring, /* name */
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
** Solidified function: time_reached
|
||||
********************************************************************/
|
||||
be_local_closure(class_Trigger_time_reached, /* name */
|
||||
be_nested_proto(
|
||||
4, /* nstack */
|
||||
1, /* argc */
|
||||
10, /* varg */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
&be_ktab_class_Trigger, /* shared constants */
|
||||
&be_const_str_time_reached,
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[12]) { /* code */
|
||||
0x88040103, // 0000 GETMBR R1 R0 K3
|
||||
0x78060007, // 0001 JMPF R1 #000A
|
||||
0x88040100, // 0002 GETMBR R1 R0 K0
|
||||
0x24040305, // 0003 GT R1 R1 K5
|
||||
0x78060004, // 0004 JMPF R1 #000A
|
||||
0x88040103, // 0005 GETMBR R1 R0 K3
|
||||
0x8C040306, // 0006 GETMET R1 R1 K6
|
||||
0x880C0100, // 0007 GETMBR R3 R0 K0
|
||||
0x7C040400, // 0008 CALL R1 2
|
||||
0x80040200, // 0009 RET 1 R1
|
||||
0x50040000, // 000A LDBOOL R1 0 0
|
||||
0x80040200, // 000B RET 1 R1
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
** Solidified function: next
|
||||
********************************************************************/
|
||||
be_local_closure(class_Trigger_next, /* name */
|
||||
be_nested_proto(
|
||||
3, /* nstack */
|
||||
1, /* argc */
|
||||
10, /* varg */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
&be_ktab_class_Trigger, /* shared constants */
|
||||
&be_const_str_next,
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[ 7]) { /* code */
|
||||
0x88040103, // 0000 GETMBR R1 R0 K3
|
||||
0x78060003, // 0001 JMPF R1 #0006
|
||||
0x88040103, // 0002 GETMBR R1 R0 K3
|
||||
0x8C040307, // 0003 GETMET R1 R1 K7
|
||||
0x7C040200, // 0004 CALL R1 1
|
||||
0x80040200, // 0005 RET 1 R1
|
||||
0x80000000, // 0006 RET 0
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
** Solidified class: Trigger
|
||||
********************************************************************/
|
||||
be_local_class(Trigger,
|
||||
4,
|
||||
NULL,
|
||||
be_nested_map(8,
|
||||
be_nested_map(9,
|
||||
( (struct bmapnode*) &(const bmapnode[]) {
|
||||
{ be_const_key(id, 2), be_const_var(2) },
|
||||
{ be_const_key(f, -1), be_const_var(1) },
|
||||
{ be_const_key(next, -1), be_const_closure(class_Trigger_next_closure) },
|
||||
{ be_const_key(trig, 7), be_const_var(0) },
|
||||
{ be_const_key(time_reached, -1), be_const_closure(class_Trigger_time_reached_closure) },
|
||||
{ be_const_key(tostring, 4), be_const_closure(class_Trigger_tostring_closure) },
|
||||
{ be_const_key(run_once, 3), be_const_closure(class_Trigger_run_once_closure) },
|
||||
{ be_const_key(o, -1), be_const_var(3) },
|
||||
{ be_const_key(next, -1), be_const_closure(class_Trigger_next_closure) },
|
||||
{ be_const_key(trig, -1), be_const_var(0) },
|
||||
{ be_const_key(init, -1), be_const_closure(class_Trigger_init_closure) },
|
||||
{ be_const_key(time_reached, -1), be_const_closure(class_Trigger_time_reached_closure) },
|
||||
{ be_const_key(id, 4), be_const_var(2) },
|
||||
{ be_const_key(tostring, -1), be_const_closure(class_Trigger_tostring_closure) },
|
||||
{ be_const_key(f, -1), be_const_var(1) },
|
||||
})),
|
||||
(bstring*) &be_const_str_Trigger
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user