Jump to content

Welcome to Smart Home Forum by FIBARO

Dear Guest,

 

as you can notice parts of Smart Home Forum by FIBARO is not available for you. You have to register in order to view all content and post in our community. Don't worry! Registration is a simple free process that requires minimal information for you to sign up. Become a part of of Smart Home Forum by FIBARO by creating an account.

 

As a member you can:

  •     Start new topics and reply to others
  •     Follow topics and users to get email updates
  •     Get your own profile page and make new friends
  •     Send personal messages
  •     ... and learn a lot about our system!

 

Regards,

Smart Home Forum by FIBARO Team



jgab

Recommended Posts

The EventRunner QuickApp makes it easy to write home automation rules, but also allows for development of very advanced rules

  • Schedule actions at specific times with additional conditions
  • Trigger actions when complex conditions are met
  • Interact with other QuickApps and systems
  • A complete event based programming language "eventscript" that is concise and powerful to describe concurrent automation rules.
     

Example

Please login or register to see this code.

Many more possibilities, with rules triggering on temperatures, alarm states, global variables etc etc... The rule language "event script" is designed to be reasonable easy to read. If condition before '=>' is true, then do the actions after the '=>'. If we don't have a '=>' it's just an expression that is computed, ex. for setting up variables like in the example.

 

An beta of EventRunner5 is available now at 

Please login or register to see this link.

I will also publish versions available via the  

Please login or register to see this link.

 which is probably the easiest way to install/update.

Here is v1.03

Please login or register to see this attachment.

 for download 

Many thanks to the bug hunters that helped me find problems 

@Sjakie

@Neo Andersson

@ChristianSogaard

@Pica2017

@chelson

@minsad79

 

Goals with EventRunner5

  • Backwards compatibility for rules from ER4 (as much as possible...)
  • Better error messages
  • Better debug possibilities
  • Easier to extend with custom functions
  • Some new cool scripting functions
  • Much cleaner code - which will allow me to sleep better at nights...

 

Current version is almost on feature parity with ER4. There are some bloat in ER4 I would like to skip unless people really need it...

There are most likely bugs lurking in ER5 as it is not tested as much yet (However, I'm eating my own dogfood and have started to use it...)

 

  • Please login or register to see this link.

  • Please login or register to see this link.

  • Please login or register to see this link.

  • Please login or register to see this link.

  • Please login or register to see this link.

  • Please login or register to see this link.

     - scheduling rules for time of day and dates
  • Please login or register to see this link.

     - rules running at given intervals during the day
  • Trigger rules
  • Event rules
  • Please login or register to see this link.

     - arming/disarming and triggering on alarms
  • Please login or register to see this link.

     - integration with http requests and nodeRed flows
  • Please login or register to see this link.

  • Please login or register to see this link.

  • Please login or register to see this link.

     and finding faults with rules...
  • Please login or register to see this link.

     - interactive web based terminal to play with rules on the HC3/ER5
  • Please login or register to see this link.

     - define your own :<property> functions
    • Please login or register to see this link.

  • Please login or register to see this link.

  • Recipes - common rules...

 

Known changes from ER4:

  • ER5 is not based on fibaroExtra so a lot of extra functions are not available. Especially functions defined for QuickApp. Ex. self:debugf etc.
  • '=' has less priority than '&'. In ER4 we could write 
    rule("@sunset => lamp:isOn & flag = true")
    which, if lamp was on set flag to true. In ER5 the priority makes this being interpreted as
    rule("@sunset => (lamp:isOn & flag) = true")
    which is not what we want and generates a runtime error.
    Instead, in cases like this, wrap the assignment in parenthesis 
    rule("@sunset => lamp:isOn & (flag = true)")
  • Please login or register to see this link.

  • Function to define rule is eval(...). They way eval is defined is differently. Most of this functions comes from the table 'er' that is passed to main(...). See declarations in the beginning of the main() function. A variable 'rule' is defined in the beginning of main(er) that points to the eval function so we can use rule("rule string") for backward compatibility...
  • Please login or register to see this link.

    is slightly different.
  • to be continued...
Edited by jgab
  • Like 6
Link to comment
Share on other sites

Congratiolations with birth of your new child!!

 

 

Edited by Sjakie
add error
Link to comment
Share on other sites

For me, the time with Fibaro is over, but the only thing I miss is EventRunner!
But if God hears prayer, maybe there will be an ER for HomeAssistant too :-)

Link to comment
Share on other sites

Great work Jgab, what a great gift to us users. Looking forward to the development and updates! Thanks for sharing 

Link to comment
Share on other sites

Jan good morning.

I guess I do something wrong?

Is it correct if QA starts you don't see all the rules scrolling?

Please login or register to see this code.

Does't  send message

Other errors fixed but I can't find to fix colors in debug

Error what is wrong here?

Please login or register to see this code.

 

 

Edited by Sjakie
Jan can you explain missing first argument?
Link to comment
Share on other sites

  • Topic Author
  • Introduction

     

    EventRunner5 is a QuickApp that install on HC3 systems.

    To configure and add rules go to "Edit & Preview" and click the "Edit" button

    Please login or register to see this attachment.

     

    There go to the "Files" tab and choose the "main" file

    Please login or register to see this attachment.


    Inside the Lua function

    Please login or register to see this code.


    are configurations set and  rules added.

     

    Rules are described in a script-language called 'EventScript'. EventScript is inspired by Lua but has some special extensions and add-ons.

    To evaluate event script code we need pass it to a Lua function named 'rule'

    Ex.

    Please login or register to see this code.

    (exactly how the output looks like will change as ER5 is evolved but the important part is that the result of our computation is 14)

     

    This is not really a rule, just a simple expression, and as such just returns the result.

    To create a rule we provide an expression with an arrow "=>", like

    Please login or register to see this code.

    An example could be

    Please login or register to see this code.

     

    When ER sees that it is a rule expression (contains =>) it does 4 things

    1. Compiles the expression into a rule and saves the rule

    2. Inspects the <condition> for possible triggers/events that the condition depends on. In our case it will depend on the event/trigger emitted when the 'switch' changes state.

    3. It sets up event handlers for all events found in the condition so that the rule is called whenever those events are emitted by the system.

    4. ...and then 'rule' return the actual rule object as the result.

     

    Note that before the rule definition we defined 2 EventScript variables, 'switch' and 'lamp' that we set to the deviceID's of the devices they represent (more on variables in another tutorial).

    Whenever 'switch' changes state, the rule will be called, it checks if the switch is on and if so it runs the action that is to turn on the 'lamp'

     

    EventRunner is very efficient in this respect because it will only test relevant rules whenever something changes in the HC3 system.

    We can literally have 100's of rules and it scales very well.

     

    Note. The default settings creates a separate log tag for ER logs.

    If the ER5 QuickApp has deviceId 777 the user log functions will log on the standard tag

    QUICKAPP777

    [02.11.2023] [15:26:29] [TRACE] [QUICKAPP7777]: 5 seconds 56

    and ER logs will log on 

    ER7777

    [02.11.2023] [15:26:41] [TRACE] [ER7777]: Defined [Rule:1] @@00:00:05 => i=i+1; log('5 seconds %s',i)

     

    You can easily select both tags in the console to see both, or only select the standard tag to see your rules logs.

     

    If you have rules from ER4 they are usually compatible with ER5 (or will be as I implement missing features).

    However, there are some differences:

     

    1) The language compiler is different, in particular that assignment '=' is an operator (with low priority)  and not a statement.

    This means that 

    a & b = c

    is interpreted as

    (a & b) = c

    which doesn't make sense. We can use parentheses to group it

    a & (b = c)

    which makes more sense.

    Although, using '=' inside complex expressions is not recommended.

     

    2) Integration with alarms works a bit differently. Will come back with a post on that.

    Edited by jgab
    • Like 1
    Link to comment
    Share on other sites

    Please login or register to see this code.

    Jan all this kind of rules with | are giving error, this rule I can split in 2 rules and I don't have an error

    Other rules I can't split how to solve?

    Link to comment
    Share on other sites

  • Topic Author
  • Terminal 5

    In the github repo there is a html page, '

    Please login or register to see this link.

    ' that serves as a terminal to talk to ER5.

     

    Download it and save in on your PC/Mac and open the page in your browser.

    There is a requirement that the browser disables cross-origin settings. On the Mac it's done in the Developer settings in Safari. 

    Here is a page with various methods for Chrome 

    Please login or register to see this link.

     

    Please login or register to see this attachment.

     

    Warning! 

    When the script starts up it will try to access the HC3 and the default user/pwd is probably wrong and you risk to be locked our from the HC3 if the script makes multiple wrong attempts to login. I would recommend to go into the script and change the default values (I will try to come up with a better model)

    Please login or register to see this code.

    ...change the default value="..." for ip, user,pwd and qa ID.. 

    Kudos to @Sjakie that took a bullet for us ;-) 

    Edited by jgab
    • Like 1
    Link to comment
    Share on other sites

  • Topic Author
  • 6 hours ago, Sjakie said:

    Please login or register to see this code.

    Jan all this kind of rules with | are giving error, this rule I can split in 2 rules and I don't have an error

    Other rules I can't split how to solve?

    The problem is the trailing ';' in the 3rd line. Remove that. Before you start a new '||' you can't end with ';'.

    It was a bit hacky in ER4 to allow that - will probably not allow in ER5.

    Edited by jgab
    Link to comment
    Share on other sites

    ok removed all ;  in of trigger

    Please login or register to see this code.

    Does this mean error?? between BERGING  and debut_weersgesteldheid??

    Link to comment
    Share on other sites

  • Topic Author
  • Try and download v0.015

    The color tags are not there. I will provide you with a print-hook that adds the colouring, and makes the rules backwards compatible...

    • Like 1
    Link to comment
    Share on other sites

    I cleaned power measuring (vey old from ER 2?) Error Time out fixed.

    v0.015 I am using??

    Thanks in advance for the color

    Edited by Sjakie
    v0.015
    Link to comment
    Share on other sites

  • Topic Author
  • EventScript basics

     

    EventScript has a lot of commonality with Lua but with some extensions for rules and "properties".

     

    Rules look like

    Please login or register to see this code.

    and the condition is an expression that should be true  for the actions to be run.

    The rule itself is on triggered when any device or variable in the condition changes value. This way ER becomes very efficient as it doesn't have to try all rules all the time. Only the rules that has conditions involving device and variables changing values.

     

    Note. Most things happening in the HC3 creates an internal event. Some of these are converted to sourceTriggers that Scenes can trigger on. ER listen to all internal HC3 events and trigger rules that have conditions depending on them. Ex switches turning on and off, dimmers getting new values, global variables changing values, quickAppVariables changing values etc. On-top of that, rules can also posted their own "home-made" events and other rules can trigger on them - more on that in another post.

     

    EventScript is designed to be concise. Many functions are defined as "properties" on resources, using the ':' operator.

    Please login or register to see this code.

    returns true if the device with deviceId 55 is on. If it's a binary switch it will return true if fibaro.getValue(55,"value") is true. If it's a multilevelSwitch it will return true if fibaro.getValue(55,"value") > 0

    otherwise false.

    Please login or register to see this code.

    will thus run the action when device 55 is turned on.

    To be more precise, when 55 is turned on the HC3 will generate an internal event with the state change. ER will listen to that and realise that the rule should be triggered. The condition is then evaluated (55:isOn) and deemed true so the action is run.

    Note. The rule will also be triggered when 55 is turned off, but the condition will be false so the action is not run.

     

    To understand if a device is on we use :isOn, to actually turn on a device we use :on

    Please login or register to see this code.

    So here device 66 will be turned on whenever device 55 is turned on. To turn on 66 ER will make a fibaro.call(55,"turnOn") call.

    There are a huge amount of the :properties that can be used in EventScript to test and manipulate most aspects of devices on the HC3 (and also other resources). The complete list is in another post.

     

    We can also work with HC3 global variables, variables defined in the General panel in the HC3 Web GUI.

    If we have a defined variable named 'DayTime' we can access it's value with '$DayTime'

    Please login or register to see this code.

    Here we check if $DayTime has the value 'Night' and then we turn on device 66. Equality test in ER is using the operator '==', like in Lua.

    Whenever the global '$DayTime' changes value the rule will be triggered and if it's value is 'Night' the action will be run.

     

    Here is an important thing to understand. If we define the rule

    Please login or register to see this code.

    in ER and restart ER, the rule will be defined. However, if  '$DayTime' at that moment already has the value 'Night' the rule will not be run.

    The reason being that there is no event generated, as events are only generated when a device/variable changes state.

    In some cases we would like the rule action to run when we restart ER if the condition is true. We can accomplish that by adding .start() to the definition.

    Please login or register to see this code.

    This will trigger the rule (manually) when it's defined, the condition will be evaluated and if $DayTime == 'Night' the action will be run.

    The reason this works is that the rule(...) function return the rule object of the defined rule. The rule object has a couple of functions defined on it, like .start()

     

    We can combine tests with 'and' and 'or', written as '&' and '|' in EventScript.

    Please login or register to see this code.

    will only run the action when 55 is turned on and the global DayTime is equal to 'Night'

     

    Time in ER is written like HH:MM, or HH:MM.SS.

    Ex. 08:00 or 10:30:30

    We can test if the current time is between (including) with the '..' operator.

    Please login or register to see this code.

    This will run the rule if the condition is true and the current time is between 08:00 and 10:30

    There are also constants for 'sunset' and 'sunset'. 

    When ER sees a time constant in a script expression it is converted to the number of seconds it represents. 00:01 is 60 seconds, and 01:01 is 3660 seconds.

    This means that we can do arithmetic on times easily

    Please login or register to see this code.

    This will check if the time is between 10min before sunrise and 10min after sunset.

     

    We would like to have variables with meaningful names instead of referring to the device 55 with its deviceId. ER scripts can pickup Lua global variables inside scripts

    Please login or register to see this code.

    works. Note that we cannot declare the Lua variables local. In that case ER will not see the variables.

     

    However, we can also declare the variables as ER variables.

    Please login or register to see this code.

    Variables assigned inside rules are created as ER variables and are available inside all declared rules.

     

    In the action part, if we want to carry out several actions, we can declare them separated with ';'

    Please login or register to see this code.

     

    Many property operators, like :on, works on list of devices. A list is defined with {...} like in Lua.

    Please login or register to see this code.

    This will apply the :on action to all the lamps in the list. The advantage is that we can have pre-defined sets of devices we use in rules.

    Please login or register to see this code.

     

    Here are some more examples

    Please login or register to see this code.

     

    more to come...

    Link to comment
    Share on other sites

    Jan,

    I have one QA, copied part by part and is now complete in ER5, both QA's are running.

    In ER5 I had last evening Time out error

    Today I find no trace of this error.

     

    Please login or register to see this code.

     

     

    Link to comment
    Share on other sites

  • Topic Author
  • Variables

     

    Variables - places to store information, comes in many shapes in EventRunner scripts.

     

    -Global Variables provided by Fibaro and set in the General Panel in the Web UI.

    (A short name for them is GVs, Global Variables)

    These variables are accessible in EventScript and is prefixed with a '$' character.

    Ex.

    Please login or register to see this code.

    They can be used in the right-hand side of the rule, the condition, as they generate events when they change state.

     

    Another feature with GVs is that they auto-convert their results. Normally, we can only store strings in GVs

    but ER we can store any value that can be json.encoded.

    Ex.

    Please login or register to see this code.

    This will json.encode the table before storing it in the GV.

    The opposite, when we access a value in a GV it will convert it back to a table in this case

    Please login or register to see this code.

    Here myTable returns {a = 42, b = 17} and then we access the .a key from that table.

    This is also true for booleans and numbers.

     

    -QuickApp Variables. Variables stored in the QuickApp and that survives restarts of the QA.

    (A short name for them is QVs, QuickApp Variables)

    Variables we normally access with self:getVariable(name) and self:setVariable(name, value) inside a QA we can

    access in EventScript with the prefix '$$'

    Please login or register to see this code.

    Otherwise they behave similar to GVs and generate events when they change value.

     

    -Lua global variables. Lua variables accessible throughout your Lua code.

    Global Lua variables defined "outside" rules are accessible inside rules

    Ex.

    Please login or register to see this code.

    Global Lua variables don't generate events when they change value so we can't use them as triggers. However, we can use them as regular variables and

    access their values for tests etc. 

    We can also not set Lua global from within EventScript. This does not work

    Please login or register to see this code.

    What will happen in this case is that an EventScript global variable with the name 'dayTime' will be created - see below.

     

    -Lua local variables. Lua variables accessible within a specified scope of your Lua code.

    A Lua locally declared variable will not be accessible in EventScript.

    This does not work

    local dayTime = 'Night'
     

    Please login or register to see this code.

     

    -EventScript global variables.

    Variables introduced in EventScript are accessible throughout all rules and expressions defined.

    Ex.

    Please login or register to see this code.

    When the ER QA is restarted and the rules re-loaded they lose their values (all ER variables are reset)

    ER variables don't generate events when they are changed, unless we declare them as "trigger variables" (TV's for short)

    Please login or register to see this code.

    Note that we provide the string name 'x' to the triggervar function. It can also take an optional second argument that the variable is initialised to.

    We can list all ER variables with the function listVariables()

    rule("listVariables()")

    This will log to the console all variables and their values.

    Note that ER has a lot of predefined variables.

     

    We can add and manipulate variables outside rules, using Lua.

    Many functions and tables that are used when working with ER is available in the 'er' argument passed to main(er). More on this in another post on how to work with rules...

    Please login or register to see this code.

     

    If we have a "Home table" with values we can easily declare the values to be accessible from within EventScript.

    Please login or register to see this code.

    defvars(table) is declared as for k,v in pairs(table) do er.var[k]=v end

     

    -EventScript local variables.

    EventScript local variables are ER variables that are temporary for a specific invocation of a rule. They are not accessible from other rules/expressions.

    Ex.

    rule("lamp:isOn => local e = env.event; log('DeviceID %s is set to on',e.id)")

    Here we declare a local variable e and store the event that triggered the rule. We then access the id key of that event.

    'e' only lives in this invocation of the rule.

    Some EventScript statements creates local variables.

    Ex.

    Please login or register to see this code.

    will create x as a local variable even if x exists as a EventScript global variable.

     

    If a variable is encountered in EventScript it's first looked up if it's an local EventScrip variable, 

    and if not we check if it's a global EventScript variable,

    and if not we check if it's a global Lua variable

    Edited by jgab
    Link to comment
    Share on other sites

  • Topic Author
  • 1 hour ago, Sjakie said:

    Jan,

    I have one QA, copied part by part and is now complete in ER5, both QA's are running.

    In ER5 I had last evening Time out error

    Today I find no trace of this error.

     

    Please login or register to see this code.

     

    Well, the keuken.wcd_WaterKoker:value returned nil so the comparison ">" threw an error.

    What is strange is the log output. Are you running v0.015?

    It should not say "Expr Runtime" but something with [Rule:x] where x is the number of the rule.

    Ex.

    Please login or register to see this code.

     

    Link to comment
    Share on other sites

    Okay Jan shoot me!

    In updater I can see only one version?

    Please login or register to see this code.

    [01.11.2023] [10:24:03] [TRACE] [QUICKAPP1048]: EventRunner5, deviceId:1048, version:0.015

     

    Link to comment
    Share on other sites

  • Topic Author
  • Working with rules

     

    Note. There is a possibility to keep rules in separate QA files instead of having all rules in QUickApp:main(er) - see "

    Please login or register to see this link.

    "

     

    When we do something like

    Please login or register to see this code.

    EventScript runs the expression as a "process" parallel in the background. The default behaviour for the 'rule' function is to print the result to the console.

    Something like

    [02.11.2023] [11:03:48] [TRACE ] [ER1249 ]: '4+5' > 9 [done]

    It will actually print both the expression '4+5' and the result 9, and add a '[done]' at the end to tell us that the process was done.

    This is what 'rule' prints to the console. What 'rule' returns is just the value 9

    Please login or register to see this code.

    However, if we suspend the process by adding a wait(1)

    Please login or register to see this code.

    What happens is that the evaluation of the expression halts for 1 second because of the wait(1) command. The rule command will return whatever the suspended expression returns, and that happens to be the string "%wait%" that our wait(1) comma returns before being suspended.

    However, after 1 second, the expression wakes up and continues, calculates 4+5 and prints to the console

    [02.11.2023] [12:03:38] [TRACE ] [ER1249 ]: '4+5' > 9 [done]

     

    So the take away from this is that there is an important difference between what 'rule' returns and what 'rule' logs to the console.

     

    When we define a rule, an expression with <condition> => <action>, our rule command returns the rule object

    Please login or register to see this code.

    'a' will be assigned the rule object. As a side effect, it will also log to the rule has been defined to the console.

     

    In most cases we are not interested of the rule object. When defined it's stored in ER and invoked when triggered by an event - that is usually what we care about.

     

    There are methods defined for rule objects that can be useful. Most of these methods return the rule again so we can chain them after a rule definition 

    like, rule(....).method1(...).method2(...).method3(...)

     

    • <ruleObj>.start(event)
      This will run the rule. Can be used to trigger a rule at define time.
      Ex. rule("55:isOn => log('OK')").start()
       
    • <ruleObj>.enable()
      Enabled the rule if it is disabled.
       
    • <ruleObj>.disable()
      Disables the rule. The rule will not trigger on events and all outstanding timers (sleeps, post etc) will be cancelled
      Ex. rule("55:isOn => log('OK')").disable() -- Start rule in disabled state.
       
    • <ruleObj>.isEnabled()
      Returns true if rule is enabled
       
    • <ruleObj>.name(str)
      Sets the name for a rule. The rule will be logged to the console as [Rule:<name>:id]
       
    • <ruleObj>.ltag(str)
      Will set the __TAG used by this rule when calling 'log'
       
    • <ruleObj>.print()
      Will print the rule
       
    • <ruleObj>.stop()
      Will stop all outstanding timers for a rule
       
    • <ruleObj>.delete()
      Will disable the rule and then delete the rule. After this, the rule is not usable anymore.
       
    • <ruleObj>.info()
      Lists all rules and what timers the rule currently have active. It can be @daily timers or future posts, or trueFor timers etc.
      This can be good to have when debugging rules to understand what is going on.

     

    When a rule is triggered, a new instance of the rule is run (as a process).

    A rule instance when logged to the console looks like

    Please login or register to see this code.

    or

    Please login or register to see this code.

    if we have a custom name.

     

    'instance' is an increasing number for every new instance of the rule started. (a little bit like Scenes on the HC2, if you remember).

    This allows us to easily pair up console logs with what rule, and what instance of the rule, the log comes from.

     

    If we have a rule like

    Please login or register to see this code.

    We may have a problem if the rule is triggered twice within 10min as both rules will run.

     

    Fortunately, ER provides commands for dealing with this.

    Please login or register to see this code.

    will kill other instances of the rule when its triggered.

    Please login or register to see this code.

    will stop the rule from running if other instances are already running.

    If mode is set to nil or any other string, the rule will allow multiple instances of the rule to run.

     

    To create a rule that turns on a lamp when a sensor is breached and then turns it off after 5min, with automatic extension of the time if sensor is breached again, the rule becomes very simple

    Please login or register to see this code.

    If sensor is breached it turns on the lamp and waits 5min.

    If the sensor is breached again during these 5min, the waiting instance gets killed (and will not turn off the lamp),

    and the new instance turns on the lamp (a no-op if lamp already on) and starts to wait another 5min

     

     

    There are some commands that can be used to inspect and manage rules with. They are available in EventScript but also also via the 'er' variable provided by the main(er) function.

     

    • er.listRules()
      Lists all rules to the consoles.
    • er.listVariables()
      Lists all EvenetScript defined variables and their values
    • er.listTimers()
      Like <ruleObj>.info() but list timers for all rules.

     

    Edited by jgab
    Link to comment
    Share on other sites

  • Topic Author
  • EventScript in depth (TBD)

     

    The complete grammar for EventScript

    Please login or register to see this code.

     

    Let's go through what we have.

     

    Symbols.

    Variable names and other names in EventScript needs to start with a letter or an underscore followed by numbers/letter/underscores.

    We can't have names with spaces in them or special characters like punctuations.

     

    Global variables.

    Fibaro global variables are accessed as $<name>, 

    Ex.

    rule("$myDay = 'work'")

    This means that if we want to access them with $ we can't have variable names with a space or punctuation in them.

    If the name contains spaces or punctuations etc. the variable can be accessed with the GV() function

    Ex.

    rule("GV('My Day').value = 'work'")

     

    QuickAppVariables

    QuickApp variables variables are accessed as $$<name>, 

    Ex.

    rule("$$myDay = 'work'")

    This means that if we want to access them with $$ we can't have variable names with a space or punctuation in them.

    If the name contains spaces or punctuations etc. the variable can be accessed with the QV() function

    Ex.

    rule("QV('My Day').value = 'work'")

     

    Variables

    ...

     

    Events

    Events are Lua tables with a 'type' key. Similar to sourceTrigger values in Scenes.

    Events can be written as #foo{a=9} and is the same as the table {type='foo', a=9}

    Just #foo is the same as {type='foo}

    Ex.

    Please login or register to see this code.

    More on event matching rules below...

     

    Constants

    ...

     

    Time

    ...

     

    Operators

    ...

     

    Expressions

    ...

     

    Function calls

    ...

     

    Time expressions

    ...

     

    List expressions (List comprehension)

    Please login or register to see this link.

    is a new feature in ER5. It's a compact way to filter and transform lists of values (Lua tables).

    The syntax is

    [ <condition to select value>,<optional expression to transform value> in <list expression>]

     

    Ex.

    Please login or register to see this code.

    The variable we use is the underscore variable '_' that is bound to each value as it iterates through the list.

    In the example we check if the value is even by testing modulo 2 is =equal to 0, e.g. _ % 2 == 0

    Then we also transform the value by multiplying by 2. So the new list will be the even values in the original list doubled.

     

    When is this useful? Assume we want to turn on the lights in a list, but only the lights that are off.

    Please login or register to see this code.

    Where we just filter the list by the lights that are off and then we call :on for those lights

     

    Want to get a list of the names of the devices with a batteryLevel < 50?

    Please login or register to see this code.

     

    Statements

    Statements are separated by semi-colon. It's actually an operator that return last expression evaluated

    Please login or register to see this code.

    will return the last expression, 3.

     

    In early days of EventRunner there was no sequence of statements and it was common to chain actions with the and operator

    Please login or register to see this code.

    However, the problem is that if any action returns false/nil the rest of the actions will not be carried out (which sometimes can be what we want)

    A sequence with ';' 

    Please login or register to see this code.

    Will always carry out all actions.

     

    Condition list (||>>)

    A syntactic version of chained if-then-elseif statements.

    rule([[keyFob:central.keyAttribute == 'Pressed' =>

              local key = key:central.keyId

              || key == 1 >> log('button 1 pressed')

              || key == 2 >> log('button 2 pressed')

              || key == 3 >> log('button 3 pressed')

              || key == 4 >> log('button 4 pressed')

     

    While-do

    Is the same syntax and semantic as in Lua.
    while <cond> do <statements> end
    Ex.

    Please login or register to see this code.

     

    Repeat-until

     Is the same syntax and semantic as in Lua.
    repeat <statements> until <cond>
    Ex.

    Please login or register to see this code.

     

     

    For expressions

    Same as Lua.

    for var=<start>,<stop>[,<step>] do <statements> end

    for index, value in ipairs(<list>) do <statements> end

    for key, value in pairs(<list>) do <statements> end

     

    The variables are automatically declared local to the rule

     

    If-then-else

    Same as Lua.

    if <cond> then <statements> end

    if <cond> then <statements> else <statements> end

    if <cond> then <statements> elseif <cond> then <statements> end

    ...any number of elseif's can be chained...

     

    Return statement

    A rule can at any time stop executing by doing return

    Normally we are not interested in any value the rule returns.

    However, there is an exception. If the rule returns the constant BREAK it will stop rules coming after being triggered by the same event.

     

    Local declaration

    ...

     

    Daily rule (@ rules)

    ...

     

    Interval rule (@@ rule)

    ...

     

    Event rules (# rule)

    ...

     

    Complex rules

    ...

     

    ...to be continued

    Edited by jgab
    Link to comment
    Share on other sites

    22 hours ago, jgab said:

    Working with rules

     

    When we do something like

    Please login or register to see this code.

    EventScript runs the expression as a "process" parallel in the background. The default behaviour for the 'rule' function is to print the result to the console.

    Something like

    [02.11.2023] [11:03:48] [TRACE ] [ER1249 ]: '4+5' > 9 [done]

    It will actually print both the expression '4+5' and the result 9, and add a '[done]' at the end to tell us that the process was done.

    This is what 'rule' prints to the console. What 'rule' returns is just the value 9

    Please login or register to see this code.

    However, if we suspend the process by adding a wait(1)

    Please login or register to see this code.

    What happens is that the evaluation of the expression halts for 1 second because of the wait(1) command. The rule command will return whatever the suspended expression returns, and that happens to be the string "%wait%" that our wait(1) comma returns before being suspended.

    However, after 1 second, the expression wakes up and continues, calculates 4+5 and prints to the console

    [02.11.2023] [12:03:38] [TRACE ] [ER1249 ]: '4+5' > 9 [done]

     

    So the take away from this is that there is an important difference between what 'rule' returns and what 'rule' logs to the console.

     

    When we define a rule, an expression with <condition> => <action>, our rule command returns the rule object

    Please login or register to see this code.

    'a' will be assigned the rule object. As a side effect, it will also log to the rule has been defined to the console.

     

    In most cases we are not interested of the rule object. When defined it's stored in ER and invoked when triggered by an event - that is usually what we care about.

     

    There are methods defined for rule objects that can be useful. Most of these methods return the rule again so we can chain them after a rule definition 

    like, rule(....).method1(...).method2(...).method3(...)

     

    • <ruleObj>.start(event)
      This will run the rule. Can be used to trigger a rule at define time.
      Ex. rule("55:isOn => log('OK')").start()
       
    • <ruleObj>.enable()
      Enabled the rule if it is disabled.
       
    • <ruleObj>.disable()
      Disables the rule. The rule will not trigger on events and all outstanding timers (sleeps, post etc) will be cancelled
      Ex. rule("55:isOn => log('OK')").disable() -- Start rule in disabled state.
       
    • <ruleObj>.isEnabled()
      Returns true if rule is enabled
       
    • <ruleObj>.name(str)
      Sets the name for a rule. The rule will be logged to the console as [Rule:<name>:id]
       
    • <ruleObj>.ltag(str)
      Will set the __TAG used by this rule when calling 'log'
       
    • <ruleObj>.print()
      Will print the rule
       
    • <ruleObj>.stop()
      Will stop all outstanding timers for a rule
       
    • <ruleObj>.delete()
      Will disable the rule and then delete the rule. After this, the rule is not usable anymore.

     

    When a rule is triggered, a new instance of the rule is run (as a process).

    A rule instance when logged to the console looks like

    Please login or register to see this code.

    or

    Please login or register to see this code.

    if we have a custom name.

     

    'instance' is an increasing number for every new instance of the rule started. (a little bit like Scenes on the HC2, if you remember).

    This allows us to easily pair up console logs with what rule, and what instance of the rule, the log comes from.

     

    If we have a rule like

    Please login or register to see this code.

    We may have a problem if the rule is triggered twice within 10min as both rules will run.

     

    Fortunately, ER provides commands for dealing with this.

    Please login or register to see this code.

    will kill other instances of the rule when its triggered.

    Please login or register to see this code.

    will stop the rule from running if other instances are already running.

    If mode is set to nil or any other string, the rule will allow multiple instances of the rule to run.

     

    To create a rule that turns on a lamp when a sensor is breached and then turns it off after 5min, with automatic extension of the time if sensor is breached again, the rule becomes very simple

    Please login or register to see this code.

    If sensor is breached it turns on the lamp and waits 5min.

    If the sensor is breached again during these 5min, the waiting instance gets killed (and will not turn off the lamp),

    and the new instance turns on the lamp (a no-op if lamp already on) and starts to wait another 5min

     

     

    @jgab Hello Jan..This "killOthers" functionality is more or less the substitution for truFor rule right?

    Because the goal of this rule 

    Please login or register to see this code.

    is to turn on the lamp on sensor breach and turn it off after 5 min, BUT if there is an inbetween motion detected, the rule will not turn off the lamp, so this is equivavlent of

    Please login or register to see this code.

    am I right?

    Edited by Neo Andersson
    Link to comment
    Share on other sites

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.

    Guest
    Reply to this topic...

    ×   Pasted as rich text.   Paste as plain text instead

      Only 75 emoji are allowed.

    ×   Your link has been automatically embedded.   Display as a link instead

    ×   Your previous content has been restored.   Clear editor

    ×   You cannot paste images directly. Upload or insert images from URL.

    ×
    ×
    • Create New...