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

@jgab

May I suggest few tips...

  1. Instead of code or in parallel to code could you please post QuickApp itself. Could be exported and easily uploaded by users like VD and no need to create buttons and labels. (Don't see control button code)
  2. Instead of using  fibaro.call() function to call QuickApp takes ~5 seconds!!!! (I have reported about it to fibaro) you can use self  which is an object that can be used inside the Quick App class. It is a reference to ‘itself’. Similar solutions can be found in programming languages such as python or this in javascript.  For example:

fibaro.call(plugin.mainDeviceId,"secure")

use

self:secure()

executed immidieatly .

 

 

Overall I'm impress of your capacity and work. 

 

Edited by cag014
Link to comment
Share on other sites

26 minutes ago, AR27690 said:

 

Now I'm totally confused, you said and I quote:

 

 

But may be I'm wrong... for other hand have tried to use your door lock code (my friend has HC3) and we've lost on buttons and labels creation. I do believe some code in button control required, right?

Anyhow thanks for your enthusiasm. 

  I think all below functions should be in button control code for each button... try it

Please login or register to see this code.

Please login or register to see this spoiler.

 

Edited by cag014
Link to comment
Share on other sites

  • Topic Author
  • 1 hour ago, cag014 said:

    @jgab

    May I suggest few tips...

    1. Instead of code or in parallel to code could you please post QuickApp itself. Could be exported and easily uploaded by users like VD and no need to create buttons and labels. (Don't see control button code)

    Yes, maybe I should. It was just  unfinished code/examples. When the QDs become more complex I will start to post the whole package.

    (A side note to @AR27690, this is a thread for sw developers, when we make ready-to-use QDs and upload them to the download area - then we add value to regular users)

     

    Quote
    1. Instead of using  fibaro.call() function to call QuickApp takes ~5 seconds!!!! (I have reported about it to fibaro) you can use self  which is an object that can be used inside the Quick App class. It is a reference to ‘itself’. Similar solutions can be found in programming languages such as python or this in javascript.  For example:

    Thanks, I'm well aware of the concept but I was to into it to realise that I also had the class defs ended up in self, I just saw the fibaro provided funcs like updateView,debug etc. ... Everything completely makes sense now and I will update the example.

    1 hour ago, AR27690 said:

    But may be I'm wrong... for other hand have tried to use your door lock code (my friend has HC3) and we've lost on buttons and labels creation. I do believe some code in button control required, right?

    Sorry, will attach the QuickApp device to the original post so you can just upload it (and by downloading the FQA you agree to be a SW developer :-) )

    Edited by jgab
    Link to comment
    Share on other sites

    2 minutes ago, jgab said:

    Yes, maybe I should. It was just  unfinished code/examples. When the QDs become more complex I will start to post the whole package.

    (A side note to @AR27690, this is a thread for sw developers, when we make ready-to-use QDs and upload them to the download area - then we add value to regular users)

    Excellent... agree

    Link to comment
    Share on other sites

  • Topic Author
  • Another topic that is not HC3 specific - but relevant again because the http functions moved from scenes to VDs (or QuickApps).

    Disclaimer: a long post...

     

    In VDs there used to be FHTTP function that was synchronous. I.e. the call (:GET :POST etc) returned the result immediately.

    Please login or register to see this code.

    In QuickApps we have the net.HTTPClient() function that used to be in scenes on the HC2. net.* are asynchronous. The request returns immediately and we provide call-back functions that the request will call when the result returns.

    Please login or register to see this code.

    Here the success resp. the error functions are called when the result arrives. This means that you need to 'chain' the requests throughout your program in some way - a "setTimeout loop" is one such model where function calls itself (very short chain).

     

    It has been numerous posts on this topic in the past. One example:

     

    Anyway, people trying to convert old VDs that used FHTTP to QuickApps with net.* will have to deal with this somehow.

    Here is a "design pattern", a way to structure a program in general (and QuickApps in this case) to deal with asynchronous calls. In this example a net.HTTPClient():request call.

    Please login or register to see this code.

    The program fetches the time for our location every 1 minute and updates a text label in the UI.

     

    So, the simple idea is that we have a main() function that have "handlers", functions that gets called for each type of event it receives. In a language with a switch statement we had used that - in Lua, tables of functions is the nest best (it's more efficient to lift out the table from main, but for this example it's ok)

     

    We have 3 types of events, 'loop', 'success', and 'error'. We have defined a function 'post' that takes an event and optionally a time and calls main(event) with that event (after the optional time)

     

    So the logic is that we start from QuickApp:onInit() by posting the event 'start'

    main's 'start' handler calls the http function and gives the events that should be called when there is a success or an error.

    On 'success' we update a label with our time (we get from the http call) and post a new 'loop' event in 1 minute - to start over.

    On 'error' we print an error message and post a 'loop' event in 2min - trying again unless we have got 3 consecutive errors, then we stop the loop.

     

    One could argue that this example is as easy if we just chain the success handler of the HTTPClient():request directly. Or a simple setTimeout loop of 1 minute calling the http function. However, in the latter case we can't be sure that the result have arrived before we try again in 1 minute - we have to wait for the response somehow.

     

    For clarity it's still good to list the 'events' that happens in the program in one place, instead of having functions spread out through the program that call each other.

     

    However, the real advantage comes when we need to do many different asynchronous calls in specific orders.

    Assume that we update the time every minute as in the example, but before we do that we need to call and get the timezone. So we have to wait for one asynchronous call before we can start our loop with another asynchronous call.

     

    The example above is trivial to extend.

    Please login or register to see this code.

    We extend main() with handlers for events 'start', 'tz', and 'err_tz'. 

    Instead of calling 'loop' from :onInit() we call 'start'.

    The 'start' handler make a timezone request and tells the http function to post a 'tz' event on success or a 'err_tz' event on failure

    On a 'tz' event we continue to call 'loop' starting the main 1 minute polling for time.

     

    So this model of having an event handler (main) and a post function allows for structuring of the program.

     

    It's easy to extend to run several loops in parallel. Here we leverage the fact that events can be of one type and have different values.

    Please login or register to see this code.

    Here we kick off 2 loops with a 4 and 6 second delay - reusing the same "loop handler" in main.

    With many call-backs being asynchronous in QuickApps (all the action methods and UI callbacks) it can make sense to always post to a common handler to get a clear structure on what's going on.

     

    Anyway, this is a way to structure your program when you find yourself doing many different things in the QuickApp and they rely on asynchronous results (run in kind of "parallell")

     

    It is also possible to achieve more advanced event handling like "joins" of events etc... That is discussed in the link in the beginning of this post and uses a

    Please login or register to see this link.

    that is not as easy to read imho .

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

  • Topic Author
  • Here is a functioning version of the user profile scheduler that was demonstrated in the post 

    I don't consider it complete. There are some limitations.

    -You can only schedule a profile once per day.

    -The time input buttons are a bit quirky.

    -It doesn't persist the schedules, so they are lost if the QD restarts. (May come in an update)

    Please login or register to see this attachment.

     

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

  • Topic Author
  • ...and

    Please login or register to see this link.

     

    • Thanks 1
    Link to comment
    Share on other sites

  • Topic Author
  • On 2/12/2020 at 3:59 PM, cag014 said:

    @jgab

    May I suggest few tips...

    1. ....you can use self  which is an object that can be used inside the Quick App class. It is a reference to ‘itself’. Similar solutions can be found in programming languages such as python or this in javascript

    You had access to the QuickApp manual ?

    Link to comment
    Share on other sites

    2 minutes ago, jgab said:

    You had access to the QuickApp manual ?

    Yes.. good catch

    Edited by cag014
    Link to comment
    Share on other sites

  • Topic Author
  • Task: Looping in a QuickApp, because we need to read a value regularly from an external device with http or something similar...

    Looping is often done with setTimeout

    Please login or register to see this code.

     

    If we always want the same time delay we can instead use 'setInterval'

    Please login or register to see this code.

    which is shorter and sweeter... it's like setTimeout but it will repeatedly call the function at the interval specified. 

    setInterval returns a reference so that we can cancel with clearInterval(<ref>).

    setInterval also exist on the HC2 - been there since setTimeout was introduced.

     

    This is ok, but the setInterval function suffers from the same problem as the setTimeout loop above - it will drift if we don't compensate for the time the code inside the loop takes.

    Sometimes that doesn't matter - but sometimes it does and I been on a crusade to inform people about it because it can cause very rare bugs... and they are the worst.

     

    We could redefine setInterval to try to be less "drifty". This works resonable. The millisecond part can fluctuate a bit but I don't dare using os.clock - now it tries to stay within the second at least.

    Please login or register to see this code.

     

    Edited by jgab
    Link to comment
    Share on other sites

  • Topic Author
  • Here is an example of using the code for polling for triggers shown in an earlier post

     

    This is a QD that looks for an event (trigger) of type 'PluginProcessCrashedEvent' and report that in the QD UI (it will list the 3 last events)

    Please login or register to see this image.

    /monthly_2020_02/crash.png.37e379d1bf3ae2a761185237c90d5d9c.png" />

    Optionally it will do a 'sendPush' to the user ID defined in the device variable (quickVariable) 'pushID'.

     

    This is very handy as it will easily give an overview what devices are crashing as you experiment with coding. When debugMessages for scenes will start to work we could easily extend the DQ.

     

    QD's that crash will be automatically "restarted" after a ~minute by the system so there will be additional crashes - an extension would be to not push repeated errors.

     

    Please login or register to see this attachment.

    • Thanks 1
    Link to comment
    Share on other sites

  • Topic Author
  • I've earlier uploaded a fibaroAPI file that can be used to call the HC3 from a PC/Mac/Linux while testing and debugging functionality on the HC3.

     

     

    With this API you can do stuff like

    Please login or register to see this code.

    Most of the fibaro.* APIs works - some stuff are not implemented yet but on the todo list. (contributions are welcome)

     

    It also supports QuickApps

    This will work

    Please login or register to see this code.

    It looks and it behaves almost as a QuickApp - however it only exists offline and doesn't have a deviceID. Not terrible exciting, but we could have coded a loop calling netHTTPClient() polling for some external value and debug if we get the right thing, manage to decode with json.decode  etc.

     

    The hc3_emulator.start(nil,2000) needs to be called last in the code as it starts running the timers needed to make setTimeout working.

     

    A bit more interesting is if we make a "proxy" QuickApp on the HC3 that looks like this This is outdated. Proxies are automatically generated and deployed on the HC3 with the hc3_emulator.proxy=true keyword

    Please login or register to see this code.

    (downloaded version here

    Please login or register to see this attachment.

    )

    It has a UI with 2 buttons and a slider. Buttons are called 'button1' and 'button2' and slider is called 'slider1'

    Instead of declaring methods on QuickApp we declare the functions using the provided function ACTION - both UI functions and device actions. The ACTION methods send the event back to the code in the IDE... or rather, it's post a customEvent on the HC3 that is picked up by the offline code that polls refreshState/...

     

    This is a binary switch so it needs to react on turnOn/turnOff

     

    If we have this QD and the deviceID is 21, we can run this code offline in our IDE:

    Please login or register to see this code.

    hc3_emulator.start(21,2000) means get events from HC3 device with id 21, and poll the HC3 every 2000ms. 1000 will poll every second - but when debugging there's no reason to stress the HC3...

     

    Now if we click on UI buttons or change the state of the device it will call the correct functions in our offline code(!) We use the UI of the QuickApp on the HC3 but all the code runs offline in our IDE. Because we also trap all the actions we also run the turnOn/turnOff or other action handlers in our IDE.

     

    self:updateView works sometimes, your mileage may vary - hopefully they will fix the bugs in the next release.

     

    net.HTTPClient() is also supported. 

    This makes it relatively easy to code and debug QuickApps offline. Just being able to set breakpoints and inspect values helps a lot... 

    Btw, I use

    Please login or register to see this link.

     

    P.S I will extend this to support scenes when we get the next update and have fibaro.getSourceTrigger. Before that it's quite useless.

     

    There is a potential bug that if events are generated too fast on the HC3 the IDE will miss some - but I'm working on a fix. Fixed.

    New version of fibaroapiHC3 0.6 - fix bug with updateProperty

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

  • Topic Author
  • Oops, the last fix for the fibaroapi makes events pile up on the HC3 - wait for a patch...

    Ok, now it's fixed. Everytime the offline QuickApp starts it clears stale events, and events are removed when received.

    The events will show up as "PROXY<deviceID>_<sequence number>" in the customEvents panel, but will be removed as soon as used.

    Edited by jgab
    Link to comment
    Share on other sites

  • Topic Author
  • When developing QuickApp scenes offline using the fibaroapiHC3 code in previous post, a nice trick is to structure it like this:

    Please login or register to see this code.

    The symbol 'dofile' is not defined on the HC3 so the code between if dofile ... end is only run when we develop the code offline.

    In the beginning we include the 'fibaroapiHC3.lua' file that gives us "HC3" compatibility.

    We also create a file 'credentials.lua' that will be loaded and assigned to hc3_emulator.credentials when we start up.

    Please login or register to see this code.

    Here we set the credentials needed to access the HC3, ip,user,pwd.

    We can also add other fields. Like 'secret' here. 

    It's defined as the value '$CREDS.secret' in the header because the credentials file is not loaded when the header is run. It will later be resolved to the value in hc3_emulator.credentials.secret - so the quickVar will get the right value.

     

    The reason to go through this trouble is that we can copy the code 'as-is- without any changes and paste it into the HC3 QuickApp editor and it works. 

    Not having credentials visible in our main code also reduces the risk that we accidentally copy and paste the code to the forum (or upload to Github) with our credentials visible - have happened to me :-) 

    Edited by jgab
    Link to comment
    Share on other sites

  • Topic Author
  • On 2/16/2020 at 1:33 PM, petrkl12 said:

    @jgab Do you know how to get device ID in QA?

     

    Please login or register to see this code.

     

    Edited by jgab
    Link to comment
    Share on other sites

    @jgab I have this bug in fibaroapiHC3.lua with timers: "fibaroapiHC3.lua:206: attempt to index global '_gTimers' (a nil value)"

     

    here is testing code:

    Please login or register to see this code.

     

    Link to comment
    Share on other sites

  • Topic Author
  • 38 minutes ago, petrkl12 said:

    @jgab I have this bug in fibaroapiHC3.lua with timers: "fibaroapiHC3.lua:206: attempt to index global '_gTimers' (a nil value)"

     

    here is testing code:

    Please login or register to see this code.

     

     

    Thanks for finding a bug. I had copied/pasted clearTimeout from ER which needed a tweak to work here.

    I fixed it in v0.12 that is linked in the post (at least I can run your example)

     

     

    3 minutes ago, jgab said:

     

    Thanks for finding a bug. I had copied/pasted clearTimeout from ER which needed a tweak to work here.

     

    Yes, but it doesn't cancel the timer - wait for 0.13 with a fix...

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