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

Posted (edited)

As when the hub.sleep() is executed the script (QA or scene) is non-responding I was wondering what can be used instead in situation when we have to wait of condition to be met and then continue.

Something like that:

Please login or register to see this code.

An example can be waiting for sensor to return certain value. Busy loop is bad solution, because it consumes CPU and checking the sensor unnecessary thousands of times.

 

Solution can be dividing a code to a parts and use events and handlers (o.e. eventLib by Mr Jan Gabrielsson) to check our sensor. But when we have to read the same sensor more than once, situation complicates. It can be managed by a variable holding a process state (in which step we are now), but this lead to complicated and harder to debug code.

 

Instead - a solution based on setTimeout comes to me. It can look like that (will not work just like that, this is sketch):

Please login or register to see this code.

This eliminates necessity of holding a state of the process and seems to me elegant, simple. The code can be divided to functions representing stages of activity, but one function holds only one stage (not many, selected by a value of progress variable - as with the events seems to be necessary).

No sleep(), no while..., minimum cpu time - and we can built the linear scenario: wait...check...do...wait...continue! Thanks to doing steps in callbacks, we can write code that behaves linearly, even though it's asynchronous under the hood.

I think it can be very useful and it would be possible to go as far as construct a small, nice framework serving in such tasks (on example waitUntil with user-set maximum waiting time to met the condition, even queuing conditions and more).

 

What do you think?

 

 

Edited by Łukasz997
Posted

You still poll the sensor (or rather "run the test"), with "delay" interval. Yes, you don't block, but you are instead sampling what the state is with regular intervals (delay), running the risk that some events happens between your sampling points...

 

What you instead want to do is hook into the event mechanism of the QA, with ex. refreshStateSubscriber, or roll your own.

Then you have callbacks to your code when an event of your interest happens.

Ex1. a device changing state - your callback checks if the event says that the light was turned on and then do whatever it should do.

Ex2. a device changes state, your callback checks if the event says that the sensor is turned off, if so you start another timer to do something if the sensor is off after 5s.

        if another events comes in during that time that says that the sensor is on, your clear the previous timer...

 

This way, your code becomes reactive on what's happening in the system, as opposed to "sampling" values at regular intervals and risk missing events.

 

You won't miss events because they queue up until you read them.

 

The EventLib library does these things, with some syntactic sugar...

 

  • Like 2
  • Topic Author
  • Posted (edited)

    The drawback is that the device is still asked "on demand" by getValue (seems to me a bit unreliable, indeed). I have no idea about the technical construction of event when state changes comparing to the asking device about state.

    Has it ever happened that an event was missed by an event handler?

    Sampling doesn't seem like a drawback to me, if the sensor enters a stable state - depends on application.

    The advance is that it's very simple.

    Edited by Łukasz997
    Posted

    If someone flicks a light switch, off-on, it's easy to miss it.

    Your code turns off the light 5min after it's turned on, but you miss that the user flicks the switch and would like to prolong the time.

    Devices with CentralSceneEvent (remote controls, key pads) don't have a state you can poll, only sends events. AccessControlEvent (Locks) usually have state (locked or not) but also other events that you could be interested in....

     

    If you have a poll interval of 1s, it could sometimes take longer because the box decides that there is something more important to do than running your QA :-) so you miss events...

    • Thanks 1
    Posted (edited)

    Example...

    Please login or register to see this code.

    It may not be as practical as the callbacks value3078 and value3079 is hardcoded here,

    and it's also hardcoded for devices' property update events.

    but one could invent other mechanisms to register what event the callback is interested in. The EventLib uses a more elaborate patter matching against the event transformed to an sourceTrigger, for flexibility and readability.

    Edited by jgab
    Posted

    Doing an event handler that does something when device 3080's value been true for 5min would look like

     

    Please login or register to see this code.

     

    Posted
    3 hours ago, jgab said:

    If someone flicks a light switch, off-on, it's easy to miss it.

    Your code turns off the light 5min after it's turned on, but you miss that the user flicks the switch and would like to prolong the time.

    Devices with CentralSceneEvent (remote controls, key pads) don't have a state you can poll, only sends events. AccessControlEvent (Locks) usually have state (locked or not) but also other events that you could be interested in....

     

    If you have a poll interval of 1s, it could sometimes take longer because the box decides that there is something more important to do than running your QA :-) so you miss events...


    Actually you can poll the "hold" event of centralevents I think 🤔 (I used to do that before your eventlib) :D

     

  • Topic Author
  • Posted (edited)

    I didn't know that managing the events is so simple... It's a real advance in my knowledge of HC3.

    When we realise quite long "script" of command one after one in a linear way (triggered when certain events occurs) - it may be harder to manage such queue of activity. Especially when events from the same sensor has influence on script at more stages than one. This is my case - when I leave/enter home the queue of commands is fairly extensive.

    I think this is a space for some improvements. Please see the small framework for managing this.

    The idea is:

    - (1) define steps of "script" in a separate functions

    - (2) define handler(s) managing incoming events

    - register (1) functions in a form of queue defining the order of executing

    Managing the order, advancing script, calling the functions etc are separated from functions (1) and (2). Framework cares about this. (1) cares only about a logic of certain step.

    Framework has few commands to manage the flow as stop, pause, restart, goto certain step, loop script. Also timeout for auto advance to next step can be defined.

     

    Here's the example. I don't know if it's useful. If few think of it like that, I'll pu together a description and code.

    Please login or register to see this code.

     

    Edited by Łukasz997
    Posted (edited)

    This is a long post, but a subject that is close to my heart :-) 

     

    So, In practice you are putting together what mathematics/computer science calls an

    Please login or register to see this link.

    - defined states and defined transition between those states due to some input. The input could ex be a sequence of events.... or a paper strip with numbers as in the famous

    Please login or register to see this link.

    .

     

    Dealing with events, that may come or not, you have identified that you need timeouts. But in the general case you need a transition from every state for every input or you can get stuck. Either you transition to an error state, and in both cases you need to decide where to go after that to "restart" the automata (or script sequence).

    The general problem (in Math too), is that any automata to do something interesting tends to be huge as so many states and transitions need to be defined to make it complete....

    ...or one could cheat, by leaving states and transition out because you know the use case, but you can't leave out more than the stuff should work, ex. recover when you end up in the wrong state...

     

    This kind of pragmatic way is what I call "event based programming". 

    The states are the event handler functions defined. The input is the events that come in. And the defined transitions are defined in code, as the event handler functions themselves can post a new event.

     

    The problem with the EventMgr as is, is that we are very limited in the handlers we can define - it's a QuickApp function with a name that identifies the event. In my case the property value + the deviceId. In your case it seems to be the centralSceneEvent's keyAttribute + deviceId - which means that you need a different EventMgr to put together the function name it should call.

     

    So, I have in my libraries moved to another approach. Defining a function that takes 2 argument. An event "pattern", telling what events it should match, and a second argument that is the function handler that should be called if we have a match. Something like

    Please login or register to see this code.

    It's important that the first argument is a pattern. We can do

    Please login or register to see this code.

    and it will match every event with a deviceId, if they have the right type, property and value. 

    The handler then gets called with the actual event it matched against, and that has the deviceId we print...

     

    There are computer programming languages that are built-up on this kind of principle, match and action, like

    Please login or register to see this link.

    and

    Please login or register to see this link.

    ..

     

    This only defines input -> actions.

    However, if we add a function to post our own event, we can create new inputs.

    input -> action -> input

    or "transform inputA to inputB

    With the new EventMgr

     

    Please login or register to see this code.

     

    We add pattern/handlers pairs. When an event is posted to the EventMgr we look up which handler has a pattern that match and call that handler. Besides that the EventMgr also subscribes to HC3 internal events and post them with the same post function.

    If we have many eventHandlers that match the same event

    Please login or register to see this code.

    They both match the event {type='device' id=88}, but the first handler returns true, which stops the matching of handlers defined afterwards. Without that we would get "MATCH" printed twice. This is a nice option to have, but sometimes we want both handlers to run. see later...

    This becomes very powerful and flexible.

     

    Assume we want to have device property change events that we can handle, similar to the first post.

    We define 2 handlers

    Please login or register to see this code.

    The first handler transforms the complex 'DevicePropertyUpdatedEvent' event into an event that looks like the sourceTrigger value we get in a scene when a devices changes property value.
    {type='device', id=<id>, property=<name>, value=<value>}

    That handler in effect transforms an input or, input -> action -> input

     

    After that we can define any number of handlers for property changes 

     

    Assume we have a keypad device, that generates CentralSceneEvents, we can do
     

    Please login or register to see this code.

    The first transforming the complex to a simpler event that we re-post, 

    and then our key handler that trigger on keys. In this case any key from any devices, but we could have had a more specific pattern and action

    Please login or register to see this code.

    So if we press key 2 on key device 854 we turn on device 743

     

    So, we have input -> action -> input. How about state? Things you wanted to do one state at a time, or in sequence.

    Well, it depends on the use case.

    Assume that we want to look for the keypress 2-3-2 and it should only be max 2sec between the presses. When the key combination is pressed we call a function to unlock a lock.

    The state here is where in the sequence we are

    Please login or register to see this code.

    Now, the state is made up of the local State and Last variables and the transitions are defined inside the handler (depending on key pressed)

     

    We could abstract this further, by introducing explicit states, something like your sequence.

    First we change the CentralSceneEvent handler so it adds a 'last' key to the event. How long since the last key was pressed in seconds
     

    Please login or register to see this code.


    Then we add a handler that transform key presses to combination lock keys and a timeout parameter if the last press was too long ago. In this case 3s
     

    Please login or register to see this code.


    Then we can make something similar to your "sequence"...
     

    Please login or register to see this code.

     

    Now this setup is hardcoded for a specific device ID and key combination. We can abstract it even further by doing a combo function that creates the event handlers for us, for a specific combination of keys (this code assumes at least 2 keys in the keycode or it will crash)
     

    Please login or register to see this code.

     

    The point is, build up a library with the level of abstraction you need. Make it a QA file and plop it into your QAs.

     

    My EventLib and other attempts are just an evolution of this code... 
    Actually, what should be improved is the post function that looks up what event handler that matches. Now it loops through all patterns, but it could be smarter and hash the patterns so it doesn't need to test them all every time... That is what EventLib and my other libs do, but it requires more code..

    Another improvement is to be able to post an event at a specific time in the future... which allows you to build schedulers... scheduling when stuff should be done during the day...


    Having these primitives in place it's pretty straightforward to define 'sequence' and 'all' handlers, like
     

    Please login or register to see this code.

    ...but it's left as an exercise to the reader ;-)

     

    Also, have a look at this thread that I started 8 years ago (It was for the HomeCenter 2, but the principles are the same)



     

    Edited by jgab
    • Like 3
    • Thanks 1
    Posted (edited)

    With little effort we can make the EventMgr more efficient by keying the event patterns on the event type. This means that we only need to match against patterns with the same type as our incoming event.

    Please login or register to see this code.

     

    It's possible to make it even more quick to look up the handlers to test with but that's becoming more complex...

    Edited by jgab
  • Topic Author
  • Posted

    Shortly after presenting above framework for "scripting within a script" I realised that something essential is missing.
    As an event manager I presume ready solution being not a part of my FW, which can filter and pre-process events.

    But this is not enough. FW will be useful when user politely follow the script queue: press button, open door, close door etc. But what if he fall into madness, because of let's say a dirty fork (see Python, but Monty: The Dirty Fork)? And become hitting button again and again; all remaining commands will be executed after he smash button few times (because one of the active event handlers receives series of events from button and in turn - having no instructions - executes series of steps; also those related to opening the door)

     

    The solution is to establish a strict bind: script step <-> the certain event.

    So, in my idea missing part is: the dynamic event manager (with dynamic filter), servicing only one event at a time with the event details defined by the script step itself.

    Please login or register to see this code.

    Actually, the current step event listener should be one and only event listener active at the moment.

     

    My idea is to built a linear script with absolute minimum user have to do with events, and I want it so much that eliminate this at all... Of course user can deal with it, because event is passed to the step function, but actually the chain input->transition->output has broken logic.

    This learned me being more careful sharing my ideas here: either it has already been done, or someone can do it better.

    • 4 weeks later...
  • Topic Author
  • Posted

    To end this topic - I've reworked the script manager and tested on some basic house scripts.

    I think that it's useful, because it's 100% event driven and make the description of the process as short as possible and easy to understand for common user.

    All the logic, filtering the events, executing user functions etc are maintained by script library.

     

    Here's the example. The goal is: (1)after pressing the button unlock the door if locked, if already opened skip step(2) or, (2)wait for door to open, (3)wait for door to close and then lock the door:
     

    Please login or register to see this code.

     

    Just 16 lines of code! When script has higher requirements, some "extensions" are available (o.e. timeout).

    The same using any event library: ~80 lines and need some programming skills. It have to maintain the transitions from step to step. I don’t want to criticise the event library, I just think that for this particular purpose my lib is sufficient.

    • Like 1
  • Topic Author
  • Posted (edited)

    This excites me more (thanks, @jgab for some inspiration), so I continue :). I added some new features.
    Here's a slightly more complex example: open the lock, wait for the door to open, then wait until closed (if already opened - skip), secure the lock, open the yard gate, wait n seconds, then close the yard gate.
    Since the Gerda door lock often freezes the QuickApp due to synchronous hub.call (sometimes even for a minute, sometimes not), I added support for asynchronous step calls (up to user decision).
    There's also a trick using a timeout and a never-committed event to "simulate" waiting - this is also asynchronous.

    Please login or register to see this code.

    Simple event manager can receive now comparison operator, user function can be passed for matching, negation can be used (any but not <parameter>), at one step can be defined many patterns for events etc. I have very little knowledge about eventLib, but probably it can be used here instead of my manager, much more enhancing the usability.

    I find this library really helpful and a big time-saver.

    Edited by Łukasz997
    Posted (edited)

    So, I'm trying to understand this a bit...

    1. Why don't you have a sleep statement instead of waiting for a non existent id? May be the same, but it would make it clearer... The autoexec here is just an unconditional wait?

    2. What happens if doorSensor is breached again directly after wait_door_close.., typically you would like to restart the wait_for_gate_close, or if button pressed2 again...

    2.1 Here you wait for the doorSensor to be safe, and then wait another 60s until you close gate, even if user press button again or doorSensor is breached again?

    3. For some events, where the event is really saying that a device is changing property/value, the step could check if the property/value the event is waiting foe is already true, or if not, wait for the event. Thus not needing "skip step" logic of start_process

     

    What is the semantic of addStepAsync? Is it running the function with a setTimeout(fun,0) ? Why do you need it? Isn't every step asynchronously waiting for the next event anyway?

     

    My general worry, is that this "list of instructions that is advanced by events (and optional timeout)" is having difficulties with events not coming in the expected order (or not coming at all) and you get stuck in some state. In principle you would need a timeout for every step, wait_door_open may never happen before user press button again etc.

    Also, there may be flows where you wait for one of several types of events to start something, and have to restart steps if nothing happens and it will start to become complex...

    Ex. How would this  usecase look like?
    1. If either motion is breached or door is opened -> Then turn on light in room -> Turn-off light when motion sensor safe for 5 min and door closed.

     

    Btw, what I use in my daily automation and is a time saver for me, is my EventRunner QA (something like eventLib on steroids)
    In EventRunner the above usecase looks like

    Please login or register to see this code.

    Defining the use-case as 2 rules instead of a single flow, allows for any of the 2 rules to start.

    1. if the light is off and a sensor is changed...

    2. or the value is already on and we wait for the condition to turn it off.

     

    So, here the optional .start() tagged onto the rule definitions will start the rule instead of waiting for an event to start the rule. It's kind of your skip-step logic as it doesn't wait for the event, but starts the rule to check the sensor values. In this case it may not be so bad to miss that first event, but it could be something like

    Please login or register to see this code.

    $timeOfDay is a fibaro global variable. The rule will start when it gets an event that the variable changed value. However, if we restart the rule after the variable is changed we will not get an event until next. day for the light to come on. Thus the .start() that runs the rule at definition/restart and it will check if the var is 'morning' and then turn on the light.

    The 'trueFor' function takes a time and any complex expression, and waits until the expression is true for the given time. It makes sure that the time is restarted if the expression becomes false while waiting.

     

    If I try to translate your example to ER it would look something like

    Please login or register to see this code.

    Which would raise my concerns as stated above, what happens if remote is pressed again before the other steps are completed? What if doorSensor is re-opened before gate is closed etc... which would led me to add some timeouts and resets of the flow if necessary. Resets may involve undoing what have been done, like securing something that been unsecured etc...

    Edited by jgab
  • Topic Author
  • Posted (edited)

    Thanks for trying to understand the library and for your in-depth comment.

     

    Every step has its own definition of the event that must occur before the step is executed. The library listens for just one event at a time (which can be a combination of several conditions — the step function is triggered as soon as one of them evaluates to true).

    Please login or register to see this code.


    So there's no need to worry about what happens if the button is pressed again etc.- it won't affect current step because it commonly listen for another event. And even though there are gotoStep and timeout mechanism (timeout allow user to define where to jump, optionally), the script runs in a linear fashion.

    The {autoExec=true} flag means that the next step should be executed immediately, without waiting for event. It can also be used with gotoStep, optionally.

    I do not use sleep() because it makes script frozen. My lib has yet no dedicated 'pause' mechanism implemented - that's why I use a 'trick'. It's very easy to add it and I'll make it soon.

    Async calls is working by setTimeout(0, fn), as you suspected. It's because when hub.call freezes, it blocks entire script (also no advancing the listener to a new event). Used only when necessary – like with the Gerda lock, which can always be turned by hand if it gets stuck.

    I'm not worried about events not arriving. However, the lib supports global timeout with an optional fallback function:

    Please login or register to see this code.

    After global timeout (and after fallback), script - based on loop parameter - ends or begin with first step.

    The library doesn't yet support something like "true for X time", but it's relatively easy to add within the current design - I'll implement it once I encounter a case that really needs it.

     

    I’m aware of EventRunner, though I’m not deeply familiar with it. My goal was to create something simple and useful, yet still capable of using LUA’s power. This is the essence of everything I’ve learned on this forum - and beyond - over the past year, beginning with one big NIL in my brain.

    ER is different class of tool, I hope. 

     

    Edited by Łukasz997
    Posted

    I was not meaning to use fibaro.sleep. I was meaning introducing an instruction/step called sleep as you used the autoexec with timeout as an unconditional sleep in this case as I understood it.

     

    So, you can't have two scripts running on parallel then? Ok.

     

    So, just to get a better understanding. How would this usecase look like with your script?
    1. If either motion is breached or door is opened -> Then turn on light in room -> Turn-off light when motion sensor safe for 5 min and door closed.

     

  • Topic Author
  • Posted (edited)

    Points 1 and 2 in this case are easy:

    Please login or register to see this code.

    This waits until incoming event meet any of two event definitions, then summon litTheLight().

    'Meet definition' means that everywhere in the event structure is present given pairs key-value.

     

    Problem begins with point 3. The lib itself has no tool to handle "trigger is true for a period of time". My first thought is to implement 'hidden' or 'system' script step which is equipped with timeout and condition is simply negated ('not equal' condition is built in my event manager). I haven't thought it completely, but in the case of more complex events, it might not work.

     

    At this stage of script lib the point 3. have to be handled by step function.

    And my intention is not to built macro engine (probably simple compiler with grammar, tokenizer, parser) for resolve the event handler pattern. Not my level.

     

    Edited by Łukasz997
  • Topic Author
  • Posted (edited)

    I didn't think clear yesterday. If we want to check o.e. if state of a device == true over some time its enough to check if new event with value other than true comes from this device within time window.

    So your example can look like this:

    Please login or register to see this code.

    neq means value of a key not equal. If sensor has only true, false states condition also can be newValue=false and second line will we more clear.

    Second line listen for events from door sensor or motion sensor and if it comes, turns off the light. And if no events, after timeout also turns off light.

    Its drawback is the little lack of clarity, but it gets the job done.

    Edited by Łukasz997
    • 4 months later...
    Posted

    Hi Guys,

    I have read this topic. And I have a problem to solve that I have no idea how to solve.

    I want to check if the local protection parameter (of the double switch device) changes its state. Do you know there is some event that triggers to check if the local protection parameter is changed? Any help would be greatly appreciated. Thank in advance!

    Posted
    1 hour ago, Herni said:

    Hi Guys,

    I have read this topic. And I have a problem to solve that I have no idea how to solve.

    I want to check if the local protection parameter (of the double switch device) changes its state. Do you know there is some event that triggers to check if the local protection parameter is changed? Any help would be greatly appreciated. Thank in advance!

     

    This QA with the eventlib from Jgab could do it on a smart implant :D 
    And maybe point you in a direction so you could use it 

     

    Please login or register to see this attachment.

    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...