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


Recommended Posts

  • Topic Author
  • 12 hours ago, jgab said:

    I’m sorry I need to test it when I’m back home - you have a fix early tomorrow morning. /J

    Ok, there was a bug if _REMOTE was set - thanks for spotting that!

    I have pushed a new version, with EventRunner.lua and EventRunnerDebug.lua

    /J

    14 hours ago, FrankT said:

    Need some more help. The alarm scene I have been using is a modified scene from Bastian (mkshb.de) and I use Fibaros UBS scenActivation.

    I've tried to use EventRunner but failed so far because I don't know how i can get the scenID.

    rule("sceneID = ???????).

    Please login or register to see this spoiler.


    Ok, the scene above would look something like below using eventscript rules.

    There seems to be missing some functions like unarmAllSensors() etc, but if they are global lua functions they can be called from within the rules.

    Please login or register to see this code.

    However, another way is to keep the lua code for the alarm scene but integrate it with the framework so that you can co-exist with other rules.

    Just have an eventscript rule to trigger on the right scene activation code and call the alarm lua code.

    Then it would look something like below (note thet 'rfidreader' must be global for it to be accessible from within the rule).

    Please login or register to see this code.

     

    Edited by jgab
    Link to comment
    Share on other sites

  • Topic Author
  • .

    The EventRunner framework is developed so that it can also run offline and be easily debugged. During the holidays I have been extensively rewriting the logic how I emulate the setTimeout functionality offline. The way I do it now is to use Lua's concept of coroutines which gives a lot of benefits.

     

    One such benefit/side-effect is that the framework now can run most standard scenes straight off inside the framework.

    Example. If we have this standard scene that loops with fibaro:sleep and prints a message:

    Please login or register to see this code.

    If we place that scene in the same directory as EventRunner.lua, and call it 'Test1.lua', and then setup EventRunner's main() like this

    Please login or register to see this code.

    ..we can run the scene. The magic is handled by _System.loadScene("Test1",11,"Test1.lua") that loads the scene from 'Test1.lua', gives it the name 'Test1' and scene id 11. Because the scene has the '%% autostart' header it will be run, and we get the log output below:

    Please login or register to see this code.

    So, we see that 08:00:51 the scene Test1 is loaded and the started.
    The scene starts by printing a welcome message. The debug output is annotated with the name of the scene and the instance number (Test1:1). Then there is a log output when the scene calls fibaro:sleep, and we see it sleeping 5 minutes for 5 times.

    This is actually pretty interesting. We can run scenes off-line. Start several instances of a scene and start them by sourcetriggers.
    Most Lua functions needed are available to the scene:

    Please login or register to see this code.

    Another example.
    We have 2 scenes, Ping and Pong

    Ping.lua:

    Please login or register to see this code.

    Pong.lua:

    Please login or register to see this code.

    They are very simple. The Ping scene (id 11) triggers on autostart and sends a message to the Pong scene (id 22) with a counter that it also stores in a global variable. The Pong scene recieves the message, prints it out and calls Ping back.
    We load the files in EventRunner:

    Please login or register to see this code.

    and we get the log output

    Please login or register to see this spoiler.

    ...the scene behaves as expected. If not we could have set breakpoints in the scene and traced it.

     

    In summary, this is a very convenient way of debugging (complex) scenes...

    We can run scenes but we can't run VDs (yet) - and many scenes interact with VDs. One way around that is to enable _REMOTE and delegate the sourcetriggers from the HC2 back to the EventRunner scene (via the event server). That would mean we could run the VDs on the HC2 and the scene offline. 

     

    Note. EventRunner.lua can't run inside EventRunner yet

    Edited by jgab
    Link to comment
    Share on other sites

    On 12/28/2018 at 12:35 AM, jgab said:

    eventrunner

    Thank you, will implement and test later today. (Do you ever sleep?)

    Link to comment
    Share on other sites

  • Topic Author
  • Just now, FrankT said:

    Thank you, will implement and test later today. (Do you ever sleep?)

    It happens.

    Link to comment
    Share on other sites

    @jgab Thanks for correction! Your framework is unbelievable!

     

    I have some problems with Log function. There is not possible to use "%"

     

    I tried:

    Please login or register to see this code.

    but in ZB there is error in EventRunnerDebug in row:
     message = string.format(message,table.unpack(args))

     

    Link to comment
    Share on other sites

  • Topic Author
  • 3 minutes ago, petrkl12 said:

    @jgab Thanks for correction! Your framework is unbelievable!

     

    I have some problems with Log function. There is not possible to use "%"

     

    I tried:

    Please login or register to see this code.

    but in ZB there is error in EventRunnerDebug in row:
     message = string.format(message,table.unpack(args))

     

    Log uses string.format and that function uses '%' for formatting directives. In this case it complains that there are no argument to the '70%' formatting directive.

    If you want to print a '%' character you have to print '%%'

    Please login or register to see this code.

    and it will come out as "Battery: 70% "

    Link to comment
    Share on other sites

  • Topic Author
  • I have uploaded a convenience wrapper for EventRunner named SceneRunner.lua (available on GitHb subdirectory /scenes)

    It makes it easy to configure scenes to run them offline for debugging.

    There are some configuration variables to setup in the beginning - similar as for EventRunner

    After that,  setup list of scenes and commands. Scenes will be loaded, and commands will be carried out after the scenes are loaded. Ex. to set off triggers etc.

    There are also some test scenes from

    Please login or register to see this link.

    in the sub-directory.

    When running in ZBS one can set right-click on SceneRunner.lua in the left hand "Project" field and set SceneRunner as "Set As Start File"

    Then open the scene you are testing and set break points and run debug, and it will step into the scene.

    Please login or register to see this code.

    I have run GEA 6.11 inside SceneRunner so it will handle some pretty advanced scenes. It may be difficult to step through GEA or even understand where to set breakpoints, so it may be of limited value. However, it is easy to let GEA speed through a couple of weeks in just seconds to see if things are done when expected, as well as setting off triggers at various time. If you develop GEA plugins it's of course possible to set breakpoints in the plugin code and step through them to see that they do what is expected too. 

    Some scenes are very noisy in the log output. There is a global '_DEBUGHOOK' that can be set to a function taking a string as argument and returning a string. It is called just before fibaro:debug outputs its messages and can be used to transform or filter the output. 

    Ex. to get rid of the GEA debug messages starting with "... check running"

    Please login or register to see this code.

     

    Edited by jgab
    Link to comment
    Share on other sites

    Moved to EventRunner 1.10. Working in ZBS, but got errormessage in HC2

     

    [DEBUG] 17:20:39: 2019-01-13 17:20:39.660264 [ fatal] Unknown exception: /opt/fibaro/scenes/312.lua:173: attempt to call global 'osOrgDate' (a nil value)

     

     

    Link to comment
    Share on other sites

  • Topic Author
  • 1 hour ago, FrankT said:

    Moved to EventRunner 1.10. Working in ZBS, but got errormessage in HC2

     

    [DEBUG] 17:20:39: 2019-01-13 17:20:39.660264 [ fatal] Unknown exception: /opt/fibaro/scenes/312.lua:173: attempt to call global 'osOrgDate' (a nil value)

     

     

    Sorry, I was too fast this afternoon and didn't check on the HC2.

    I have pushed a new version of EventRunner.lua with the fix.

     

    Link to comment
    Share on other sites

    On 10/25/2018 at 7:56 PM, jgab said:

    In scenesToWatch it should list the scene ID of the iOSLocator scene. You main scene will then ping it every minute and if it is not running it will restart the iOSLocator scene.

    Testing this by stop iOSLocator scene but i cannot see that it restarts after 1 min. 

    Link to comment
    Share on other sites

    @jgab is it possible in ZBS to simulate sceneActivation:scene. I have tried to test this program snippet you helped me with, but I don't know how to 'inject' scene=21?

     

    -[[
    %% properties
    228 sceneActivation
    %% events
    %% globals
    %% autostart
    --]]
    -- Don't forget to declare triggers from devices in the header!!!
    _version = "1.10"  -- fix5, Jan 13, 2019 

    --[[
    -- EventRunner. Event based scheduler/device trigger handler
    -- Copyright 2018 Jan Gabrielsson. All Rights Reserved.
    -- Email: [email protected]
    --]]

    …….
    rfidreader = 228

    checkWindows = true   -- true 

    ---------------- Here you place rules and user code, called once --------------------
    function main()
      local define, rule =  Util.defvar, Rule.eval
      local devs = json.decode(fibaro:getGlobalValue(_deviceTable)) -- Read in "HomeTable" global
      Util.defvars(devs)                                            -- Make HomeTable defs available in EventScript
      Util.reverseMapDef(devs)                                      -- Make HomeTable names available for logger

      
       rule([[rfidreader:scene==21 =>
                 || globalalarm == 'armed' >> unarmAllSensors(); log('unarmed')
                 || globalalarm == 'unarmed' >> log('unarmed2 ');post(#unarmed)]])
                 
      rule([[#unarmed => 
                 || checkWindows >> log('checkWinAndDoors'); checkWinAndDoors() ;;
                 armAllSensors(); log('armed')]])

     
      function checkWinAndDoors()
        fibaro:debug('checking')
      end
      function armAllSensors()
        fibaro:debug('arming')
      end
      function unarmAllSensors()
        fibaro:debug('unarming')
      end

    end -- main()

    Link to comment
    Share on other sites

  • Topic Author
  • 45 minutes ago, FrankT said:

    @jgab is it possible in ZBS to simulate sceneActivation:scene. I have tried to test this program snippet you helped me with, but I don't know how to 'inject' scene=21?

     

     

    Please login or register to see this code.

     

    3 hours ago, jompa68 said:

    Testing this by stop iOSLocator scene but i cannot see that it restarts after 1 min. 

     

    The iOSLocator in GitHub do respond to a {type='%%PING%%'} with a {type='%%PONG%%'}  event.

    How do the client look like? (the scene sending the ping and restarting if no answer?). Is it the same code as in the post Oct 25?

    Please login or register to see this spoiler.

     

    If so, do you get any PING or PONG log printouts?

    Link to comment
    Share on other sites

    1 hour ago, jgab said:

    How do the client look like? (the scene sending the ping and restarting if no answer?)

    No answer, i have re-copy the above code just to be sure but still no PING or PONG printouts

    Link to comment
    Share on other sites

  • Topic Author
  • 18 minutes ago, jompa68 said:

    No answer, i have re-copy the above code just to be sure but still no PING or PONG printouts

    Ok, will do some tests this evening and see if I can understand why - maybe something have changed in the framework since October.

     

    Link to comment
    Share on other sites

    I had also problem with PING and PONG after implementation directly into ER framework. I solved it via my own rules PING1 and PONG1 based on post Oct 25 ...

    Link to comment
    Share on other sites

  • Topic Author
  • 9 hours ago, petrkl12 said:

    I had also problem with PING and PONG after implementation directly into ER framework. I solved it via my own rules PING1 and PONG1 based on post Oct 25 ...

     

    Ok, to be clear. It's only the code that respond to a PING with a PONG that is built-in to the ER framework currently (that's a 1-liner)

    The logic to ping scenes and restart them is not built-in. Then you have to add code like the one from Oct.25.

    If I add that code to a recent v1.10 of the framework

    Please login or register to see this code.

    I can get it to ping my iOSLocator (my iOSLocator has id 8), and the log look like this on the HC2

    Please login or register to see this code.

    ...and it restarts scene 8 after a minute of no response.

     

    Maybe it's time to do a separate "Supervisor" scene, that looks after other scenes and restart them, start them in right order at reboot, logs and report errors, etc.

    Edited by jgab
    Link to comment
    Share on other sites

  • Topic Author
  • (I'm re-structuring the documentation for EventRunner - moved the notes on the EventRunnerLIte version here)

     

    Here is a description of the light version of the "framework" code with a simple example. The "Lite" version is a bare bone version of the full EventRunner framework but still have the advantage of the single scene style of programming. The code is also short enough to browse through and understand how it works. The full version has support for declaring Lua event handlers, EventScript, Hue lights, and more extensive offline debugging. However, the Lite version is a good introduction to the programming style used in the EventRunner framework and is useful as a base for specific scenes that doesn't need more. Ex. the iOSLocator scene is based on Lite. Lite can still sens and receive events with scenes based on the full framework.

     

    To run the framework on the HC2 set the "Max. running instances:" parameter for the scene as high as possible, e.g. 10. Normally the scene only run one instance, but when external triggers (devices, globals etc) come in, temporary scene instances are started and terminated. Also set the scene to "autostart".

    Please login or register to see this code.

    So, in a normal scene, you get a new instance of the scene when a trigger happens. You then look at the fibaro:getSourceTrigger to determine what trigger happened and carry out the actions needed, and then the instance terminates. If you need to remember something between scene invocations you need to store it in a fibaro global variable.

    The difference is that in the EventRunnerLite framework above, the main() function gets called with every new sourceTrigger, in the same scene instance(!). The advantage is that you can keep values in local lua variables between triggers. In the above example we increment counter, a local lua variable, every time the light is turned off.
    The way to think about this is that you have a scene that is continuously running and your main()function gets called with every new event that happens. This actually makes it much easier to code complex scenes as we will see.

    Some examples (we only show the main() part of the framework)
    Example code triggering on Fibaro remote keys 1-2-3 within 2x3seconds. The KeyFob has deviceID 5 in this case.

    Please login or register to see this code.

    Here we check if 1-2-3 is pressed on a fibaro KeyFob, with max 3 seconds between keypresses. We have 2 local variables that keep the state of the last key pressed and at what time. We need to declare them before the main() function so they survive between calls to main().   

    In the framework we can not use fibaro:sleep() as it stops everything and we can not receive events while sleeping. The way to do something after a specified amount of time is to start a timer that then carry out the action, e.g. setTimeout.   

    Note that we use 'osTime' and 'osDate' here and it is just to make sure that the code also works when debugged off-line as we do some tricks with the time then. However, when running on the HC2 they are just aliases to 'os.time' and 'os.date'


    The framework has a convenience function post(event, time) that sets up a timer that then calls the main() function. The timeparameter is the number of seconds in the future this event will be posted to main().

    Having this we can add code to simulate keypress events (sourceTriggers) to see if the logic of the example above works.

    Please login or register to see this code.

    Here we also check for sourceTriggers of type autostart or other to only post our 'simulated' events at startup of the scene.

    post is not only for debugging but is a way to drive the flow of the scene. Posting and reposting an event in the future is a way to create loops.

    Please login or register to see this code.

    This starts 2 loops, one that prints "Ding! every 60 minutes and another that prints "Dong!" every 40 minutes. The trick here is that the part that catches the 'loop' event, also re-posts the event with the specified time delay.

     

    Lets create a scene that does something at specific times of the day.

    Please login or register to see this code.

    At start of the scene we go through all specified times and posts future events that we can trigger on. This is the only tricky part as we need to understand if the next event happens today or if we passed the time and the next event will be the next day. We define our own custom event of type time with the time string and the action to be carried out. We then add code that watch for that custom event and calls the action function. We also make sure to re-post the event 24 hours from now so that we continue to get the events on a daily basis.

     

    postcan post an event immediately if the time parameter is left out or set to zero. Sometimes if we post something in the future we may want to cancel that post because we changed our mind. post returns a reference that can be used to cancel the post if it has not expired. Lets go back to the 'loop' example.

    Please login or register to see this code.

    By calling cancel(ref) we cancel the last {type='loop', action='run', time=60*60, value='Ding!'} we have posted, thus effectively stopping the loop.

    An example is a sensor (id 88) that turns on a lamp (id 99) when breached and turns it off if there are no breaches with a specified amount of time. Note, need to declare 88 valuein the scene header to get the trigger.

    Please login or register to see this code.

    We also add an call event type that makes it easy to post a lua function to execute in the future.

    It is possible to code the above example with fibaro:sleepbut the advantage here is that we can easily duplicate the code in the same scene to look for different sensors and lamps - something that is tricky if we use fibaro:sleep. The only thing to be aware of is that we need different reflocals to keep track of the different posts we do.

    Please login or register to see this code.

    The code does not conflict with each other, and we can add as many as we need. However, there is a pattern here that we can leverage.

    Please login or register to see this code.

    Because we have this event driven approach, all of the examples above, the key fob checks, the scheduling of times, and the sensors and lamps can coexist in the same scene. That makes this model very powerful.

     

    Lets make an example of a simple presence simulation scene. First we have logic to see if any sensor is breached or all sensors have been safe for 10 minutes. Depending on what was detected it sends a {type='presence', state='start'}or a {type='presence', state='stop'} event that is picked up by another part of the scene that starts and stops a turning on and off lamps at random.

    Please login or register to see this code.

    The presence detection part does not start the simulation directly when all sensors are safe, rather it posts an "away" event 10 minutes into the future that starts the simulation. A breached sensor will cancel the "away" post. The presence parts reacts to the start/stop event by starting and stopping a loop that toggles lamps at random.

     

    So, this example can also be combined with all the other examples in one big scene without causing conflicts. However, it is also easy to break the scene apart and distribute them over many scenes.
    postRemote(sceneID,event) is a version of post that sends an event to another scene. If that scene is running the same framework the event will be delivered to the main() function as usual.
    Lets split the presence/simulation apart. First the detection part.

    Please login or register to see this code.

    The only thing we needed to change was the post(event) to postRemote(presenceScene,event). That will send the event to the presence scene (assume it has ID 133).
    The simulation scene does not have to change at all.

    Please login or register to see this code.

    This makes it convenient to distribute logic between scenes. In fact it is easy to do client/server type of models. A way to overcome the lack of sharable Lua libraries...

    Client.

    Please login or register to see this code.

    Server.

    Please login or register to see this code.

    The trick is that post adds the sceneID of the calling scene to the event in the field event._from. The server can then just send the result back to the scene in the event._from field and thus serve any number of clients. Other uses for this is to implement a ping/pong model between scenes for a keep-alive logic.

     

    So, to summarise the advantages of the event/single instance model:

    • 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
    • Easy to distribute different rules/logic between scenes and allow them to communicate
    • Easy to schedule actions to do in the future - that can then 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...

     

    When coding with this framework and style for a while it can be a bit tedious with all the if-then-else inside the main() checking for types of events. It is quite easy to build on this to make the coding even more convenient. I have a myself a full blown 

    Please login or register to see this link.

     framework that changes the coding model a bit. Instead of main() being called for every new trigger/event, main() is only called once and inside main() event handlers are declared that will get the events as they arrive. On top of that I have an elaborate 

    Please login or register to see this link.

     supporting many of the tasks I need to code in my home automation system.

    • Like 1
    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...