Jump to content
Guides for the Forum Read more... ×
Poradniki na Forum Read more... ×

Recommended Posts

Here is an example of how to code in a "single instance / event" style. A style I'm using today for all of my scenes.

The idea is that instead of having to deal with a new instance being spawned with every scene trigger, all triggers are dealt with from within a single scene instance that is continuously running. It becomes something close to a traditional event loop model found in most modern GUI frameworks.  The advantages with coding scenes in this style are:

  • Scene can keep state in local lua variables between scene invocations/triggers
  • Easy to keep different rules/logic in the same scene without causing conflicts, e.g. combining continuous running loops/schedules with immediate reaction on incoming triggers
  • Easy to distribute different rules/logic between different scenes and allow them to communicate
  • Easy to schedule actions to do in the future - that can be easily cancelled if new information is gained.
  • Because the scene is continuously running it doesn't matter if there is a heavy initialisation when the scene starts up (parsing HomeTables etc.) as it is only done once...
  • The framework has extensive support to run and debug the scene offline on a PC/Mac to get things right before deploying the scene on the HC2. Offline it is easy to simulate trigger/events to understand if the logic is correct, something that is not always easy to detect in a asynchronous environment.
  • It has proven to be easy to integrate with external event/msg based systems like Node-red, where scenes can both send and receive events to node-red and thus extend functionality with Alexa, Google home, Hue etc etc.

 

Please login or register to see this attachment.

 

The framework is available in two version, a 'light' version and a full blow version with a lot of bells and whistles. The latter also supports writing rules in EventScript, a "simple", but very flexible and efficient approach to writing rules that need to trigger things at various times of the day, or trigger on sensors or switches changing states. The implementation of EventScript is built on-top of the single instance framework and the event model and would have been impossible to do in a traditional scene model. 

Ex of EventScript rules.

Please login or register to see this code.

More on EventScript and the full blown version is available in the posts listed below:

  • Please login or register to see this link.

    (works for EventRunnerLite too)
  • Please login or register to see this link.

    - a bare bone version of the framework
  • 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.

    - mapping of Hue devices to standards z-wave/fibaro:* calls
  • Please login or register to see this link.

    - sending/receiving events from node-red, with extendable, example flow.
    Gives support for Sonos TTS and Alexa input... 
  • Please login or register to see this link.

    - using the EventRunner framework (SceneRunner)
  • There will also be some services based on the EventFramework posted
    • Please login or register to see this link.

      - a service that checks with iClod for people at places and sends events to other EventFramework services
    • Logger service - TBD
    • Alarm service - TBD
    • Please login or register to see this link.

      - a UNIX like crontab service other scenes can register call-backs with
    • Please login or register to see this link.

       - A scene that pings EventRunner scenes and makes sure they stay alive.
  • Best practices rules - TBD
  • Implementation notes 
    • Please login or register to see this link.

    • Notes on the EventScript implementation - TBD
  • Please login or register to see this link.

 

The lastest version of the code is kept in my

Please login or register to see this link.

. The background of this framework and a thread discussing it can be found 

Please login or register to see this link.

, however the code has evolved a bit from when originally posted there.

 

 

 

 

Edited by jgab
  • Like 5
  • Thanks 3

Share this post


Link to post
Share on other sites
  • Topic Author
  • One way to to make the framework easier is to enhance the 'post' function. 'post' not only takes time in seconds from now that the event should be posted. It is also able to post the event at specific times of the day or next day .

    Below is a variant of 'post' we can do if the time parameter is a string:

    Please login or register to see this code.

    Post uses the 'toTime' function that converts a time string to seconds from now (it could be negative if a time in the past is specified).

    Having 'toTime' we can do some simple time math with sunset/sunrise, like 'post(event,"t/sunset+15")' that will post the event 15 min past sunset.

    The time schedule example in the first post can then be easier coded as:

    Please login or register to see this code.

    A more powerful 'post' function makes it much easier to deal with time.   

    One issue is that if you try to post something to happen at sunrise tomorrow, a 'post(event,"n/sunrise")' will post tomorrow, but at today's sunrise time. This is because when the post is made, sunrise returns today's sunrise. A way around this if doing a scheduler is to have a scheduling loop that make all the daily schedules at midnight. Then sunset/sunrise will return the correct values.

    Please login or register to see this code.

    The only snag is that in the startup we have to post events happening today, because the setup loop only run at midnight. Well, we can live with that.

    Edited by jgab
    • Like 3

    Share this post


    Link to post
    Share on other sites

    Nice :) thank you very much for this!

     

    Do I understand that essentially it is a daily loop that 'posts' returning actions (although it is not really a 'daily loop' as a post triggers a repost) and that any device events (e.g. sceneactivation, value) results in posting additional triggers? 

     

    I do however not fully understand 1 thing:

    How are the additional triggers included? And how can you include multiple events from different devices? Is it as simple as including all of them in the header? But how (technically) are these then all combined in one scene instance?

     

    Small other thing: in case of failure / bug, will the scene autorestart?

     

    Thanks!

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • Yes, device events arrives as posted events. You have to declare device properties/events/weather/globals triggers you want to receieve in the scene header. I've excluded the header part from my examples. The last version of the time scheduling is actually a daily loop that at midnight post the events for each day.

    Your question, yes you include them in the header and they will all arrive as calls to 'main(sourceTrigger)' in the same scene instance (thats the magic of the rest of the framework code). So for instance, the 'presence detection' example rely on that the sensors 99,199,201, and 301 are declared in the scene header. The code react on any of them being triggered and check if the others are also safe or breached. In theory we could keep a local bit-array with the status of the sensors to directly know if they are all safe or someone is breached without re-checking all the sensor as is done in the code. However, I wanted to keep the example simple.

     

    Second question, no it doesn't autorestart - that is a bit tricky. What I do is that in my scenes I implement a 'keep-alive' mechanism. In all scenes I have a

    Please login or register to see this code.

    and in some selected scene(s) watching the others, there is a 'pong' routine that pings the other scenes and if they don't respond with a pong within certain time they are restarted.

    Please login or register to see this code.

     

    Edited by jgab
    Added delay after receiving a 'pong' - otherwise we have a lot of ping-pong...
    • Like 1

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • Another problem when coding loops with fibaro:sleep (or setTimeout for that matter) is time drift.

    Please login or register to see this code.

    The loop doesn't take 1 minute, but 1 minute + the time the stuff before the sleep requires to execute. So if you hoped to do everything on the minute things start to drift after a while. Sometimes that is not a problem but if you want thing to happen at specific times it is.

    Please login or register to see this code.

    This post the first "ding" at 15:00, and then the "ding" loop re-post the "ding" event 24 hour from now. Even though the 'printf' takes a very little time (ms), after a while it will start to drift and "dings" will start to happen 15:00:01, 15:00:02 etc.. The problem is of course that we do a relative post ("+/24:00"). One solution is to try to time how long it takes for the other stuff to execute and deduct that from the 24 hours. The easier solution here is to instead do a 'post("n/15:00")', that takes care to post at the time specified. This will run forever without drift (true for "t/..." too).

    If you want to run on a relative time advance, like every hour, you either schedule "n/00:00", "n/01:00", "n/02:00", ... etc or you need to do some calculation...

    However, the framework always do a post in a separate timer thread which kind of helps minimizing the drift. Just by doing the post(event,"+/01:00") as soon as possible, before we do the other stuff, it will minimize the drift. Even if, like in this case, the printf will take a long time...

    Please login or register to see this code.

     

     

    Share this post


    Link to post
    Share on other sites

    Thanks @jgab! thinks are getting more clear!

    (Although I still do not understand technically what in the code ensures that only one scene instance is running :) )

     

    I have been able to create triggers, but not scheduled events.. Can you help me with the following?

    For some reason this code (as part of the main function):

    Please login or register to see this code.

    does yield a posting, but nothing happens 30 secs later.

     

    Weirdness seems to happen in the debug as the posting is scheduled for the same time as the post (11:47:15):

     

    Quote

     

    [DEBUG] 11:47:15: EventMgt - EventRunner v1.0
    [DEBUG] 11:47:15: Loading rules
    [DEBUG] 11:47:15: Posting {"type":"scheduler","event":{"type":"action","action":function: 0x9851628},"next":"+/00:00:30"} for Thu Aug 16 11:47:15
    [DEBUG] 11:47:15: Scene running
    [DEBUG] 11:47:15: Sunrise 06:24, Sunset 21:02

     

    Do you have any clue what is going wrong here?

    (FYI: it seems that the mailbox variable remains empty; plus in this case the posting happens before the code reports "scene running" but the latter does not seem to be the issue, because the same happens if the scheduled action is triggered by an event)

     

    Thanks!

    Edited by 3JL

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • Ok,

    you are now using the full blown EventRunner framework, which is ok. In the 'Lite' version described above a 30s scheduled loop would look like this:

    Please login or register to see this code.

    In the EventRunner framework there are many ways to achieve this.

    First, the main() function becomes an initialization functions that runs before the scene starts - that's why you see the 'post' before the message that the scene has started, So inside main you do initialization stuff (read HomeTable, set up variables etc, and declare event handlers or script rules).

    What you do in your example is that you post an event but you have not declared any event handlers to do anything with it, so nothing happens. (the mailbox variable is only used for fibaro triggers coming in, internally posted events are handled more efficiently)

    I will come back with another post describing the syntax of the EventRunner framework better, but here are 3 ways to achieve a 30s loop:

    Please login or register to see this code.

    or because this is a very common pattern there is a Event.schedule function that creates a loop for us: (the action can be either an event or a lua function like in this case)

    Please login or register to see this code.

    or using the script rules:

    Please login or register to see this code.

     

    Edited by jgab

    Share this post


    Link to post
    Share on other sites

    Thanks! I got confused between Event.schedule and post({type=schedule,...})

    This works now!

     

    What would be the syntax for a single action within 30 secs, rather than repeated every 30 secs?

     

    edit: for e.g. the case to turn off a lamp again 30 secs after an event. My use case: I have several events that can turn on the ventilation for different periods, but they should not interfere (e.g. if it is turned on for 30 mins, and another trigger wants to turn in it on for 10 mins shortly thereafter, the 30 mins should prevail). Your framework seems excellent to manage this.  

    Edited by 3JL

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • Well, then you just post something +30s and don't re-post the event.

    Please login or register to see this code.

    Assume that you have a switch with deviceID 33 and you want to do something 30s after the switch is turned on

    Please login or register to see this code.

    or with the scripts

    Please login or register to see this code.

     

    Edited by jgab
    • Like 1

    Share this post


    Link to post
    Share on other sites

    @jgab i am testing your "bathroom" settings but i get this error when start scene. What have i miss?

    Please login or register to see this code.

     

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • 6 minutes ago, jompa68 said:

    @jgab i am testing your "bathroom" settings but i get this error when start scene. What have i miss?

    Please login or register to see this code.

     

    Does the version in example_rules.lua work? It seems like it doesn't think that wc.door is defined which is strange. Can you send me the whole main() setup code?

     

    Share this post


    Link to post
    Share on other sites
    1 hour ago, jgab said:

    t seems like it doesn't think that wc.door is defined which is strange

    Well, my own fault. Different name in "HomeTable" and code.

     

    Q2, code starts both lights in WC but does only turnOff 1 of them. Strange...

    Please login or register to see this code.

     

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • well, the 

    Please login or register to see this code.

    is one statement, so the inBathrom only affect the "tak"

    Please login or register to see this code.

    is another that is always carried out if the door is open and the motion is safe for 10min

    I would write it as 

    Please login or register to see this code.

    then if inBathroom is true it will turn off both lights.

    or

    Please login or register to see this code.

    here we move the inBathroom test to the left side so if the 'for' is true, the inBathroom need to be true too for the right hand to execute. Keep it outside the 'for' expression though.

    or

    Please login or register to see this code.

    ||>> is a kind of if-then syntax, "|| <test> >> <expression> ; <expression> ; ..."

    Edited by jgab

    Share this post


    Link to post
    Share on other sites

    @jgab in one of your examples you send notification every week at a specific day. Can your scene also handle odd or even week numbers?

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • Ok, never thought about week numbers. 

    I have pushed a new version of EventRunner.lua to Github with week number support. The "constant" 'wnum' returns the current week number. (operator '%' is reminder)

    A rule could look like this then

    Please login or register to see this code.

     

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • It makes sense to have % and wnum in the script language. However, it is quite easy to add functionality with lua functions.

    Please login or register to see this code.

    and its reasonable efficient. I just realized that os.date("%V") only works on Mac and HC2. On windows it's os.date("%W"). Need to add test in the code...

    Share this post


    Link to post
    Share on other sites

    Hi @jgab, can 'rule.eval'-scheduled actions be cancelled / overruled?

    In the example you have provided above: the action should be performed only 30 seconds after last trigger of device 33 (assuming multiple triggers can happen within 30 seconds).

     

    On 8/16/2018 at 2:04 PM, jgab said:

     

    Please login or register to see this code.

     

     

    Share this post


    Link to post
    Share on other sites
  • Topic Author
  • If the device is a switch and you want the switch to turn off when its been on for 30s one can use the "for" construct

    Please login or register to see this code.

    for(<time>,<expr>) monitors the <expr> and when it has been true for <time>  it returns true. In the example above if it turns off before 30s the timer is cancelled and started next time the device trigger on.

    if you want to monitor if a sensor has been safe for 30s and then do something it is similar

    Please login or register to see this code.

    Does that answer your question? If you have a specific case you like to share I can help you with suggestion how to express it.

    Share this post


    Link to post
    Share on other sites

    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

    ×