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


HC3 QuickApps coding - tips and tricks


jgab

Recommended Posts

1 hour ago, jgab said:

New version v0.64. New start option 'quickApp'. Connects to existing quickApp on HC3 with given id.

Please login or register to see this code.

Thanks but it doesn't work as I expected - it creates proxy device but I want to use only devices, variables, ... directly from HC3

 

Link to comment
Share on other sites

Wow... a huge bug in QucikApp??

API commands will be executed only after QuickApp finish. Same behavior that we had with scenes in HC2.

So now if you will loop in QuickApp, you will never get an answer!!??

I think on previous version it was OK.

Edited by cag014
Link to comment
Share on other sites

  • Topic Author
  • 1 minute ago, cag014 said:

    Wow... a huge bug in QucikApp??

    API commands will be executed only after QuickApp finish. Same behavior that we had with scenes in HC2.

    So now if you will loop in QuickApp, you will never get an answer!!??

    ??

    No, not a bug - if you run a thread (setTimeout) the other QA functions will be executed.

    You can't do a fibaro.sleep loop - that will block all other calls to the QuickApp.

    Link to comment
    Share on other sites

    2 minutes ago, jgab said:

    ??

    No, not a bug - if you run a thread (setTimeout) the other QA functions will be executed.

    You can't do a fibaro.sleep loop - that will block all other calls to the QuickApp.

    Correct, but if you create a loop without using setTimeout. For example I want to set a loop on refresh to catch events.

    Link to comment
    Share on other sites

  • Topic Author
  • 4 minutes ago, cag014 said:

    Correct, but if you create a loop without using setTimeout. For example I want to set a loop on refresh to catch events.

    Yes, but you do that loop with setTimeout or setInterval.

     

    fibaro.sleep or busy loops will always starve the Lua environment.

    15 minutes ago, petrkl12 said:

    Thanks but it doesn't work as I expected - it creates proxy device but I want to use only devices, variables, ... directly from HC3

     

    Sorry, v.0.65 with a fix.

    7 minutes ago, cag014 said:

    Correct, but if you create a loop without using setTimeout. For example I want to set a loop on refresh to catch events.

     

    and beware that /refreshState hangs if there are no events.

    Here is a hack to get around it

     

    Edited by jgab
    Link to comment
    Share on other sites

    5 minutes ago, jgab said:

    Yes, but you do that loop with setTimeout or setInterval.

     

    fibaro.sleep or busy loops will always starve the Lua environment.

    No, I don't use fibaro.sleep at all. Just a standard while loop.

    In order to catch refresh events , I don't think it wise Idea to use setTimeout (for how long and why to call same function again and again. This requires to to export all variables to global)

    In addition there is some different between api.get calls

    api.get("/devices/31").name   -  returns value immediately

     

    While button exection

    api.get("/plugins/callUIEvent?deviceID="..plugin.mainDeviceId.."&elementName=button0&eventType=onPressed&value=null")

    waits for QuickApp to finish.

     

     

    Edited by cag014
    Link to comment
    Share on other sites

    3 minutes ago, jgab said:

    Yes, but you do that loop with setTimeout or setInterval.

    or net.HTTPClient() imho - did not try yet but also uses callback principle so should work... But don't use the "api" calls to loop.

    Link to comment
    Share on other sites

  • Topic Author
  • 2 minutes ago, cag014 said:

    No, I don't use fibaro.sleep at all. Just a standard while loop.

    In order to catch refresh events , I don't think it wise Idea to use setTimeout (for how long and why to call same function again and again. This requires to to export all variables to global)

    No, you can use closures and it is very wise ;-)

     

    Just now, petergebruers said:

    But don't use the "api" calls to loop.

    Because it hangs or why?

    Link to comment
    Share on other sites

    In addition there is some different between api.get calls

    api.get("/devices/31").name   -  returns value immediately

     

    While button exection

    api.get("/plugins/callUIEvent?deviceID="..plugin.mainDeviceId.."&elementName=button0&eventType=onPressed&value=null")

    waits for QuickApp to finish.

    Link to comment
    Share on other sites

  • Topic Author
  • 4 minutes ago, cag014 said:

    In addition there is some different between api.get calls

    api.get("/devices/31").name   -  returns value immediately

     

    While button exection

    api.get("/plugins/callUIEvent?deviceID="..plugin.mainDeviceId.."&elementName=button0&eventType=onPressed&value=null")

    waits for QuickApp to finish.

    Because the latter sends an event. And events are taken care of when the QuickApp have time for them.

    What do you mean by QuickApp finish? You mean you exit out of an function QuickApp:function  ?

     

    You run the api.get inside the button? Then no other QuickApp:function will be called before you exit - or call setTimeout

    Edited by jgab
    Link to comment
    Share on other sites

    18 minutes ago, jgab said:

    What do you mean by QuickApp finish? You mean you exit out of an function QuickApp:function  ?

    No... when all QuickApp:function are finished.

     

    18 minutes ago, jgab said:

    You run the api.get inside the button? Then no other QuickApp:function will be called before you exit - or call setTimeout

    Not inside button. Inside QuiclApp:init()

     

    Event more weird...  have moved all api button calls to external scene. By executing the scene inside QuickApp:init() it still waits to finish the QuickApp.

     

    Edited by cag014
    Link to comment
    Share on other sites

    31 minutes ago, jgab said:

    Sorry, v.0.65 with a fix.

    Thanks, it works perfectly ?

    Link to comment
    Share on other sites

  • Topic Author
  • 7 minutes ago, cag014 said:

    No... when all QuickApp:function are finished.

     

    Not inside button. Inside QuiclApp:init()

    but the QuickApp never finish unless it is explicitly restarted - or crashes and restarts

     

    If I have this code

    Please login or register to see this code.

    and a button that has the code 

    Please login or register to see this code.

    I get a log every 4s + when I press the button - all working together.

    Edited by jgab
    Link to comment
    Share on other sites

    7 minutes ago, jgab said:

    Because it hangs or why?

    Because it is "blocking" indeed and iirc the second issue is what @cag014 mentions here: 

     

    7 minutes ago, cag014 said:

    In addition there is some different between api.get calls

    api.get("/devices/31").name   -  returns value immediately

     

    While button exection

    api.get("/plugins/callUIEvent?deviceID="..plugin.mainDeviceId.."&elementName=button0&eventType=onPressed&value=null")

    waits for QuickApp to finish.

     

    I am not sure if that is a bug, or a side effect of how FQAs work but calling the 

     

    @cag014 did you ever do any "raw" ms windows develepment? Think of Lua as running an "event loop" and everything inside your loop does "cooperative multitasking". So everything has to be fast and non-blocking. As soon as something would block or cause delays (because you want to "sleep") then you have to shift to a callback-pattern, so I agree with @jgab setTimeout to loop (using a closure as a callback function) are good...

     

    The only issue I have with the "callback paradigm" is that my brain is not well-adapted to keeping/tracking in mind "a bunch of callbacks".

     

    Let me give an example of what I mean by "callback soup"

     

    Suppose you want te scrape some information from a public website... You want to make it robust so you use net.HTTPClient() and define 2 callbacks

     

    - The first one handles success. Maybe you don't get all data in one go so now you have do another http call... This call can end in success or failure.

    - The second one handles retries. Let's say it calls setTimeout to simply "retry" the call after five minutes. setTimeout uses a callback...

     

    So far seems doable. But.

     

    Now the first callback can end in success or failure (temporary failure, site is down, data does not match, whatever) To handle failure, you have to retry maybe after five minutes so you use  setTimeout to simply retry the second http call (you don't want to retry the first one, we got the results already). If it fails too much you might want to give up and retry from the start... More setTimeout and callbacks.

     

    So, this simple case already has a few possible states and callback functions and it makes my head hurt.

     

    When you want to loop, you basically recurse...

     

    4 minutes ago, cag014 said:

    No... when all QuickApp:function are finished.

    Yeah, because it is "cooperative multitasking" the function has to finish, before Lua can handle any of the callbacks. And the callb

     

    As a C/C++ programmer I can assure this is a good thing because if Lua would interrupt your code at some random point, we would be effectively doing "multe threading" and you would get all of the downsides of that like synchronisation issues and locking!

     

    In Lua you can be sure you are running code in a block from start to finish and no one is going to change anything behind your back.

    Link to comment
    Share on other sites

  • Topic Author
  • 1 minute ago, petergebruers said:

    The only issue I have with the "callback paradigm" is that my brain is not well-adapted to keeping/tracking in mind "a bunch of callbacks".

     

    Let me give an example of what I mean by "callback soup"

     

    Suppose you want te scrape some information from a public website... You want to make it robust so you use net.HTTPClient() and define 2 callbacks

     

    - The first one handles success. Maybe you don't get all data in one go so now you have do another http call... This call can end in success or failure.

    - The second one handles retries. Let's say it calls setTimeout to simply "retry" the call after five minutes. setTimeout uses a callback...

     

    So far seems doable. But.

     

    Now the first callback can end in success or failure (temporary failure, site is down, data does not match, whatever) To handle failure, you have to retry maybe after five minutes so you use  setTimeout to simply retry the second http call (you don't want to retry the first one, we got the results already). If it fails too much you might want to give up and retry from the start... More setTimeout and callbacks.

     

    So, this simple case already has a few possible states and callback functions and it makes my head hurt.

     

    So that's why I did this post how to turn callback juggling into a structured event loop - including setting a maximum numbers of retries.

     

    Link to comment
    Share on other sites

    3 minutes ago, jgab said:

    but the QuickApp never finish unless it is explicitly restarted - or crashes and restarts

    Technically speaking I think you are right but I would say the Lua interpreter running your code never finishes.

     

    4 minutes ago, jgab said:

    I get a log every 4s + when I press the button - all working together.

    Yes, but the important part imho is this code runs fast and finishes:

     

    Please login or register to see this code.

     

    I haven't tried it but I would not do it with a "while true do sleep self:test()" in that function... You carefully selected "setInterval" in this code...

    Link to comment
    Share on other sites

    12 minutes ago, jgab said:

    but the QuickApp never finish unless it is explicitly restarted - or crashes and restarts

    This is not correct.  It looks like the service runs, but all functions of specific QuickApp instance must to finish.

     

    9 minutes ago, petergebruers said:

    When you want to loop, you basically recurse...

    That's exactly what I want to avoid.... You know how you start recurse, but you never know how it will end.

    Another question is how scene execution related to QuickApp inside HC3. As I said by running external scene doesn't help... looks like one instance of Lua runs on HC3.

     

    Edited by cag014
    Link to comment
    Share on other sites

  • Topic Author
  • 1 minute ago, petergebruers said:

    Technically speaking I think you are right but I would say the Lua interpreter running your code never finishes.

     

    Yes, but the important part imho is this code runs fast and finishes:

     

    Please login or register to see this code.

     

    I haven't tried it but I would not do it with a "while true do sleep self:test()" in that function... You carefully selected "setInterval" in this code...

    No, it won't work with a busy loop 

    If they wanted they could have started the QuickApp as a coroutine (maybe they do?) and call yield() every time QuickAccess is accessed (metatables)

    2 minutes ago, cag014 said:

    Please login or register to see this link.

      9 minutes ago,  

    Please login or register to see this link.

     said: 

    When you want to loop, you basically recurse...

    That's exactly what I want to avoid.... You know how you start recurse, but you never know how it will end.

    Another question is how scene execution related to QuickApp inside HC3. As I said by running external scene doesn't help... looks like one instance of Lua runs on HC3.

     

     

    But setTimeout & friends is not about recursion. The function calling setTimeout(f,x) exits before the function f is called.

    Link to comment
    Share on other sites

    But all that still doesn't explain why takes 5 seconds to execute API or fibaro.call inside QucikApp.

     

    Link to comment
    Share on other sites

    A bit off topic but I think we've all been on this planet for a while and I guess some of our ways of thinking are "learned" while some simply reflect how the human brain works.

     

    When I try to explain issues to my wife, I often tell her we are used to "following recipes", like preparing "béchamel sauce".

     

    Ever tried to write a recipe in "recursive style"? Or "functional style"? Maybe there is some "callback style" in recipes eg  "after 10 minutes, take the pan and stir" =: setTimout with callback :D

     

    Please login or register to see this link.

     

    No matter how hard I try (and I've written small programs in Erlang so I did try very hard :D ) to get fluent in different paradigms - I always go back to "imperative". For multitasking, I am inclined to believe "Communicating sequential processes" is a viable option, like used in Erlang and Go... but I am not a professional programmer so no experience with practical implications at all.

     

    21 minutes ago, jgab said:

    So that's why I did this post how to turn callback juggling into a structured event loop - including setting a maximum numbers of retries.

    I saw that and I would recommend other users to have a look at it. It indeed captures part of what I said...

     

    18 minutes ago, cag014 said:

    That's exactly what I want to avoid.... You know how you start recurse, but you never know how it will end.

    Yeah, and sometimes recursion works, but if you are not careful it really blows up your stack because it does not really convert to a loop - instead it really causes each function call to nest another... and another... and... Until the interpreter shuts down your process.

     

    18 minutes ago, jgab said:

    No, it won't work with a busy loop 

    Thank you for confirming that.

     

    Personally, I think it is good thing, that QuickApp:onInit() blocks every other part of the FQA. Because you would probably set things up before everything else runs.

     

    Suppose you use "X" in a button, and by some silly coincidence the user clicks when you are halfway through your QuickApp:onInit() and X has not been set up yet?

     

    2 minutes ago, cag014 said:

    But all that still doesn't explain why takes 5 seconds to execute API or fibaro.call inside QucikApp.

    Indeed it does not, but it reminds me of the 10 second "hangs" we sometimes have on HC2 when a scene gets killed.

     

    I have no access to the "guts" of a HC3 so this is big speculation, but maybe and "api call to yourself" causes the HC3 to reload/redo something, cause a time-out and a retry "behind the scenes"?

     

    Maybe it is possible to discuss that with Fibaro (through their beta bug tracker I mean)... So "api call" to another device (any typ) is OK but "api call" to yourself (your FQA) = 5 second delay.

    Link to comment
    Share on other sites

    • jgab changed the title to SDK for remote and offline HC3 development.

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