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


TriggerQA


jgab

Recommended Posts

Developing QAs there is typically a need to watch if other devices change state - or a global variable changing value etc.

Either you do a loop every x second looking if the value changes or you setup code to retrieve events from the api with /refreshStates.

The former is a bit wasteful and the latter is tedious to do and if all your QAs do it, and they are many, it could start to impact performance...

 

Here is a drop-in QA that looks for triggers with /refreshState and send the 'sourceTriggers' to other QAs that subscribe to them.

Please login or register to see this attachment.

 v1.21

It works and scales very well if you don't need high-performance event-watching. It will have a small delay due do the need to hand over the trigger to another QA. 

...but it's compensated by how easy it is to use from your QA... I have tried to do some measurements but the delay seems to be < 1 s. can't measure it with os.time().

...and it gives you all (and some) of the functionality we get from Scene's conditions.

 

The QA can be set to hidden and forgotten as there is no need to interact directly with the TriggerQA.

 

Ex.

Our QA that want's device sourceTriggers would subscribe this way

Please login or register to see this code.

This will subscribe  the 'device' sourceTRiggers and call the QUickApp:sourceTrigger(tr) function for all new incoming device events.

It will match the event pattern so we can be more specific

Please login or register to see this code.

Will only get triggers from device 88 and 99.

etc.

 

The triggers that are supported are

Please login or register to see this code.

 

It also supports time/cron events

Please login or register to see this code.

Ex.

Please login or register to see this code.

will send back a source trigger ever 15min in the format

Please login or register to see this code.

'tag' can be useful to match timers to actions...

Edited by jgab
  • Thanks 3
Link to comment
Share on other sites

Jan. publishing all /refreshstate packets to MQTT IMHO it's an easy way here.

 

Link to comment
Share on other sites

  • Topic Author
  • On 12/2/2021 at 9:18 PM, 10der said:

    Jan. publishing all /refreshstate packets to MQTT IMHO it's an easy way here.

    That's different use case.

    Edited by jgab
    Link to comment
    Share on other sites

    • 1 year later...

    I installed the Trigger QA - and also found an updated version 1.23

     

    Im a little unsure how to use your examples  _ 

    self:subscribeTrigger({type='device', id=88}) 
    self:subscribeTrigger({type='device', id=99})

     

    I have this SONOS QA were i want to use the Playing State as trigger.

    • PLAYING
    • STOPPED
    • PAUSED_PLAYBACK
    • TRANSITIONING
    • NORMAL
    • REPEAT_ONE
    • REPEAT_ALL
    • SHUFFLE
    • SHUFFLE_NOREPEAT
    • SHUFFLE_REPEAT_ONE


    Please login or register to see this image.

    /monthly_2023_04/image.png.d2aa719f1dc2001c67048eec4f9c9229.png" />

    Link to comment
    Share on other sites

  • Topic Author
  • If you have 1.23 do

    Please login or register to see this code.

    ...and you can see what events you get.

    Then you extend the QuickApp:sourceTrigger function to do the right thing with the right event...

    • Thanks 1
    Link to comment
    Share on other sites

    • 3 months later...

    Im going to ADD my garage port to my system using a Fibaro Relay Switch and a sensitive strip / door sensor.
    The Relay Switch FGS-222, configured toggle mode is wired to the gate controller.
    The gate controller has 24v output which is ideal for FGS-222 - that setup is working
     

    Please login or register to see this image.

    /monthly_2023_08/image.png.669bc5d323b413f1a68819028207f37e.png" />
    The Garage gate has no feedback if gate is open or closed, this is where the sensitive strip / door sensor is added to avoid the system is getting out of sync (open/Close status)

     

    would the Trigger QA be a good direction to go for having a QA that act as a binary switch for the garage gate?

     

     

    Link to comment
    Share on other sites

    • 4 weeks later...
  • Topic Author
  • Yes, I guess so. It would be a binary switch where the state would be the state read from the strip and then the turnOn/turnOff would be commands sent to the relay.

    Link to comment
    Share on other sites

  • Topic Author
  • So, you turn on and turn off the relay to make it start moving?

    Like

    Please login or register to see this code.

     

    Edited by jgab
    Link to comment
    Share on other sites

    • 5 months later...

    Good Morning Jan,

    please forgive if my questions are trivial:

    1. Let's assume that I have several different events to process, and it's possible that new event appear before handling the previous one is completed (like keep light on until someone is still in the room).

    I've tested this and it seems, that the event handles must of course finish the current activity before be invoked again. And the events seems to be stored in a queue.

    Assuming my observations are true the event handler can quickly become not a real time machine (fall in a state of time "debt").

    How can I help myself in such situation when I do not want to create new QA for every single event? Is it possible to establish more than one function handling the events in one QuickApp to be executed concurrently?

    2. Could you tell me how to do the QA interface act like in ER5 - I mean grow in height with more informations to display, and scroll - like terminal. I've tried to find it in the code of ER5 - and failed...

     

    By the way I admire you for an extraordinary skills.

     

    Please login or register to see this image.

    /monthly_2024_02/image.png.f19149b3bc75602068071a7009847cb1.png" />

     

     

    Edited by Łukasz997
    Link to comment
    Share on other sites

  • Topic Author
  • Yes, event handlers in a QA (or setTimeout) runs the tasks sequentially as you have noticed.

    Yes, a single handler or setTimeout can become delayed (in time debt) because an earlier handler is not finished

    Ex.

    Please login or register to see this code.

    will not run all 3 exact after 1 second. The last two will be some milliseconds delayed.

    In general this doesn't really matter, as it tends to catch up anyway and we are talking about milliseconds.

    What you should never do in handlers are sleeping or busy waits.

    Please login or register to see this code.

    fibaro.sleep() stops any tasks to be scheduled and the last two handlers becomes delayed 5s and 10s.

     

    So, the general trick is to break up event handlers in smaller tasks

    So an event handler triggering on a sensor (id 88), turns on a light and turns off the light (id 99) after 30s could be done

    Please login or register to see this code.

    would work, it's an handler that takes 30s of compute time and blocks all other handlers and create "time debt"

    The way to do it is to break it up in the 2 tasks - starting the light and turning it off 30s later.

    Please login or register to see this code.

    Here we start a new task after 30s turning off the light.

    Both tasks, the initial event triggering turning on the light, and the second turning off the light, takes a few µ-seconds each.

    This allows other tasks, coded in a similar way to run in "pseudo parallelism".

    In ER5 there are 100's of tasks active at any time and very very seldom there is a "time debt" that anyone notice.

     

    There is one place where time debt is crucial - no matter how small, and that is in timed loops, where debt doesn't even out but accumulates.

    Please login or register to see this code.

    Here the idea is to run a loop every second. However, the loop will take 1min + the time doTask() require.

    Even if doTask() takes a few µ-seconds, the loop will start to drift.

    We also now that other tasks running can cause a delay before our loop gets called.

    Ex. we can make sure that the loop start on even minutes (00:seconds)

    Please login or register to see this code.

    but because of the drift we will after a while call the loop at :01 and then :02 etc...

     

    The way to get around that is to not rely on "relative" time. e.g. +1s, but keep an absolute time when we should loop next.

    Please login or register to see this code.

    Here we keep track of the absolute time the loop should be invoked and  call the loop with how many seconds until next invocation.

    Here the loop may still be delayed by doTask() or other tasks running, and it may be delayed and miss exact on the minute,

    but it will always "recalibrate" and get back on track.

     

    Ex.in ER5 we can set rules to run on specific times during the day. If we are unlucky a rule may be a second delayed, but they always get back on track.

    And if lights turn on 1s after sunset people usually doesn't notice. 

     

    If your case needs realtime precision, which is not that common in home-automation, you probably need busy loops...

    "Near realtime" is usually good enough - and if your event handlers are well written we are talking milliseconds delays...

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

    Thank you very much for the complete solution.

    I think that using the event mechanism obligates to change the approach.

    Yours is simpler, shorter and not generating further problems.

     

    My construction of sustaining the light on is: Set the light on for xx seconds, countdown. if during "on" time the motion sensor is breached then reset timer and countdown again. Such way the "handler" can operate even a hour...

    Link to comment
    Share on other sites

    I would like to mention one more thing which may be relative to the topic of handling the events.

     

    From time to time I'm confronted with very huge delays with start executing of a scene or even in a middle of it (seconds to a minutes - sic!). When and what scene - I see no pattern in this behaviour. This happens "without a reason" at the level of particular code chunk. CPU load is very limited all the time. I can't explain this phenomenon.

     

    At the other hand in many places (concurrently executed different tasks) I've used to do the following:

    hub.sleep(1000) -- or more than 1000

    as it's a very comfortable way to delay a loop, on example.

     

    After reading your post I'm wondering if coincidence between 'hub.sleep'-s in various places can at the level of whole system build such delays?

    Do you think it's possible and this command should generally be avoided? If correct, I rebuild those loop delays to the system time comparison or something other.

    Link to comment
    Share on other sites

  • Topic Author
  • 15 hours ago, Łukasz997 said:

    Thank you very much for the complete solution.

    I think that using the event mechanism obligates to change the approach.

    Yours is simpler, shorter and not generating further problems.

    My construction of sustaining the light on is: Set the light on for xx seconds, countdown. if during "on" time the motion sensor is breached then reset timer and countdown again. Such way the "handler" can operate even a hour...

    To reset a task we need to be able to cancel an outstanding task.

    setTimeout returns a reference that allow us to call clearTimeout(ref) to cancel it before it has executed.

    So every time the sensor is triggered we clear the "turnOff task" so we can start a new "turnOff task" in 30s

    Please login or register to see this code.

     

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

  • Topic Author
  • 15 hours ago, Łukasz997 said:

    I would like to mention one more thing which may be relative to the topic of handling the events.

     

    From time to time I'm confronted with very huge delays with start executing of a scene or even in a middle of it (seconds to a minutes - sic!). When and what scene - I see no pattern in this behaviour. This happens "without a reason" at the level of particular code chunk. CPU load is very limited all the time. I can't explain this phenomenon.

     

    At the other hand in many places (concurrently executed different tasks) I've used to do the following:

    hub.sleep(1000) -- or more than 1000

    as it's a very comfortable way to delay a loop, on example.

     

    After reading your post I'm wondering if coincidence between 'hub.sleep'-s in various places can at the level of whole system build such delays?

    Do you think it's possible and this command should generally be avoided? If correct, I rebuild those loop delays to the system time comparison or something other.

    The delay between a sensor being breached and a scene getting invoked is usually small, but I know people that have had long delays ( @Sjakie ?)

    The general thought has been that it depends on a very loaded system, but I guess it can have other reasons too - the mysterious ways of z-wave...

     

    Within a given scene or quick app, if you do a sleep, nothing else is allowed to happen in that scene. No button presses, no other timers etc etc. It just freezes for the specified time.

    If you have set "Allow to restart a running scene" it will kill a sleeping scene.

    If you haven't set it it will ignore the trigger and wait until the current scene finish (no stacking up of triggers)

     

    So, if you are doing anything moderately complex I would recommend to stay away from sleeping and use setTimeout delays and/or setInterval loops.

     

    Having said that, the simplest scene to turn on a light when sensor triggered and wait x seconds with restarts if the sensor triggers again is

    1. a condition to start scene when sensor is triggered

    2. action: fibaro.call(light,"turnOn"); fibaro.sleep(30*1000); fibaro.call(light,"turnOff")

    3. set "Allow to restart a running scene" to true

    A new trigger of the sensor will kill the running scene and start a new sleep for 30s before turning off light

     

     

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

    @jgab your solution for me was to use wait(0) what solved most delays.

    Sometimes I see a delay but can't trigger why.

    IKEA is a few tends of a second slower to switch on

    The biggest gain is memory usage < 52%, I cleaned and Nowe light switched on at the moment I open the door.

    With higher use off memory I could do two steps in that room

    Edited by Sjakie
    add cleaning system
    Link to comment
    Share on other sites

  • Topic Author
  • So the TriggerQA is by now a rather old hack.

    When working with events in this way it usually comes down to a set of use-cases and best practices to do most types of automation rules.

    Run on triggers, run on specific times, cancels and restart tasks etc.

    My current best thinking in capturing this is in the EventLib library - it allows to write "rules" in Lua that have almost the functionality of ER5. ER5's event script language has a non-blocking wait that requires some thinking and extra code to get right in pure Lua.

     

    • Like 1
    Link to comment
    Share on other sites

    Thank you very much for the explanations.

     

    And one answer seems to me not touched: is it possible to establish more than one event handler in a single QA (not to build new QA for every single task)? I'm nearly sure that the answer is: one QA, one handler.

     

    I understand that in one handler there's still a possibility to examine what happened and do the proper action (not very long action...). I didn't study EventLib approach closely, bot maybe multi-handlers are be possible there. In other hand, I think this requires pre-emptive mulitasking OS at the background. I do not believe that Fibaro a system of this kind. Lack of knowledge makes me sad... :)

    Link to comment
    Share on other sites

    W dniu 26.02.2024 o 09:11, jgab napisał:

    To reset a task we need to be able to cancel an outstanding task.

    setTimeout returns a reference that allow us to call clearTimeout(ref) to cancel it before it has executed.

    So every time the sensor is triggered we clear the "turnOff task" so we can start a new "turnOff task" in 30s

    Please login or register to see this code.

     

     

    Here's how I did it. The idea behind putting it to to sleep was generating less CPU load, eliminate thousands device calls.

    local sensorID = 187 -- adres Motion Sensor
    local switchID = 92 -- adres Walli Switch
    local delay = 45
    local breachedTime = hub.getValue(sensorID, "lastBreached")

    hub.call(switchID, 'turnOn')

     

    while os.time() < breachedTime + delay do
      hub.sleep(2000)
      breachedTime = hub.getValue(sensorID, "lastBreached")
    end

     

    hub.call(switchID, 'turnOff')

    Edited by Łukasz997
    Link to comment
    Share on other sites

  • Topic Author
  • Are we talking about QAs or scenes? TriggerQA is for QAs so this discussion may be in the wrong thread…

    Anyway, there is no reason for a scene to loop and waste cpu like that if you follow my 1-3 recipe in my earlier post.

    why thousands of cpu calls?

    Link to comment
    Share on other sites

    Godzinę temu, jgab napisał:

    Are we talking about QAs or scenes? TriggerQA is for QAs so this discussion may be in the wrong thread…

    Anyway, there is no reason for a scene to loop and waste cpu like that if you follow my 1-3 recipe in my earlier post.

    why thousands of cpu calls?

    My solution is not a QA. I list my solution just to illustrate, why I use sleep. Elimintating sleep will lead to many getValue calls. That's all.

    I know your recipe generates totally minimal load to the system.

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