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


  • 1

HC3 coding - first impressions


jgab

Question

I have been playing around with the HC3 and tried to program some scenes/devices. This is my experience so far.

Disclaimer1: My review is based on the latest beta firmware, v5.020.xx, and there may be improvements coming before the ink in this post has dried.

disclaimer2: My personal preferences when it comes to API design and programming styles shines through in my review… others may have other preferences.

 

Scenes

They have changed quite a lot, some Lua functions and some fibaro* functions have disappeared -

Please login or register to see this link.

 

So scenes have a new way of declaring triggers. No more the Lua comment section in the top of the scene. Instead a separate section of the scene that contains a "Lua table" expression that checks various events/states. If the total expression is true, the action part of the scene is executed. Technically, the scene structure that you get from api.get("/scenes/<SceneID>").content returns a struct with two parts; a conditions part with the condition structure and an actions part with the Lua code that makes up the action part.

Is this an improvement? I don’t know - seasoned Lua programmers could make these tests in the Lua code previously anyway and without having been able to dig too deep into this there is usually some interesting trade-offs and subtle sources of errors/unintended consequences when doing these kind of trigger expressions when it comes to stateless and stateful events….

 

Is it easier for non-Lua programmers? You judge, here is a condition for a device and a time:

Please login or register to see this code.

(block scenes make use of the conditions support - but I'm not reviewing the block scenes as the implementation seem to be a bit unfinished in the current release)

 

Because scenes miss net.HTTPClient(), fibaro.getSourceTrigger(), fibaro.sleep(), os.time(), os.date(), clearTimeout() I haven’t played around so much with scenes, as some of my standard scenes were not portable without major rework.

Edit: The above functions are now part of scenes. Note that fibaro:getSourceTrigger() is now a variable, 'sourceTrigger'.

 

Noted though that they changed from fibaro:* to fibaro.* and some names have changed, like  fibaro:getGlobalValue has changed to fibaro.getGlobalVariable etc. deviceID can be a list, so fibaro.call({66,77},"turnOff") works.

 

Devices

This is a new animal and has received  most of my attention.

 

Devices you code yourself are called QuickApps.

 

When you create a new QuickApp device, you can choose what type of device it should be; binary switch, binary sensor, multilevel switch, temperature sensor etc. …and it will be treated as a “native” device of that type.

You can call fibaro.call(<myDeviceID>,”turnOn”) on a QuickApp device of type binary switch etc.

 

However, you are limited to the available types - more on that later.

 

To code a QuickApp device you now have QuickApp “class” that provides a some functions that makes up the Devices interface.

Please login or register to see this code.

Above is a simple binary switch you can turn on/off. :onInit() is called when the device is saved or restarted - good place to setup stuff or start loops (with setTimeout)

 

You extend the QuickApp class with methods being the actions that the device should accept. Here ‘turnOn’ and ’turnOff’.

 

It turns out that you can define

Please login or register to see this code.

and then from another scene/device call fibaro.call(<deviceID>,”doWhatever”,5,10)

and your function gets called. This is seriously cool as it means that we have a nice way to pass arguments between devices and from scenes to devices - something we didn’t have before.

The other cool thing is that the function is called in the same thread as is started with :onInit()…. This requires some effort from the design perspective (not like in the old scenes when just another instance was created).

This also means that

Please login or register to see this code.

will count up x every time fibaro.call(<deviceID>,”incX”) is called. We have stateful devices...

 

So, ‘QuickApp’ is a “class” that we can extends with some methods to handle actions on devices.

 

There are also possibilities to define handlers for the QDs UI elements (button etc).

If you have a button named ‘button1’ you can define

Please login or register to see this code.

and it will be called when the button is pressed (similar for sliders)

 

There maybe other QuickApp:* methods too but I haven’t discovered any more yet.

 

Then there is the self:* methods…

 

Inside the QuickApp:functions() there is 'self', a variable available with our QuickApps methods.

‘self’ is not the same as ‘QuickApp’ and should not be confused (however in some prototype based object implementations for Lua, the “class” and the “self” is the same object, so it can be confusing). QuickApp is the class, and 'self' is a variable holding our instance of that class.

 

I have found some useful methods.

Please login or register to see this code.

Note here that the UI that can be defined have the same buttons in sets of 1 - 5 buttons, slider, and static text - doesn’t seem to be any enhancements here. It may be extendable but I guess it needs to be supported in the mobile apps too.

 

Another improvement is that Devices have their own “global variables”. In practice a property table associated with the Device structure. We have functions like:

Please login or register to see this code.

 

We can do setVariable/getVariable for variables belonging to our device. They survive restarts and they are also saved when we download and distribute with a QuickApp VD file. In that way we can see them as a generalisation of the IP: and Port fields in old VDs - Here we can define them ourselves and use them as initialisation parameters for QDs.

 

There may be more self:* functions but I haven’t seen them so far.

 

Besides this we have almost the same Lua and fibaro functions as we had in scenes in the past. No synchronous HTTP functions anymore - only the asynchronous net.HTTPClient().

 

The api.get(“/refreshState?<lastID>”) api seems a little more extensive - it seems to report more types of events - like global variables changing states.

 

And there is a new concept <CustomEvents>.

They are of type {name = <string>, userDescription=<string>}

They can be defined with api.post("/customEvents",{name=name,userDescription=descr})

and “emitted” with api.post("/customEvents/"..name)

I think there is a fibaro.emitCustomEvent(<name>) function available too.

 

Scenes can probably trigger on them and they show up in api.get(“/refreshState?<lastID>”)

 

Because events are “statically” defined - name and user description - you have to be a bit creative with naming schemas to use them as general message events - but it’s doable. I will come back on this topic and why you would like to have that... I have played with them to implement a kind of broadcast function...

 

Summary

So, all-in-all, QDs opens up for some new interesting programming paradigms.

- What I miss is a more extensive network library to easier integrate with external systems/devices. Nothing much have happened with the net.HTTPClient() library in 7 years…

- I would have liked  more UI elements and layout options than the buttons and sliders we have had for some years now.

- I would have liked Lua metatables, loadstring, and coroutines too - it’s 2020 and the security issues should be solvable (there are so many other more low hanging security risks anyway)

- Possibility to enable remote Lua debugging - using

Please login or register to see this link.

?

- Now, some more advanced programming has to be realised as QDs, and thus need to have device types like binary switch. For some code like schedulers or other helper scenes it doesn't make sense to squeeze it into a device model - they are not really devices. At least an 'other' device type would be nice to have.

 

This was a first impression - some good news and some stuff that could Improve.

Some small issues I have been wrestling with:

- There are some glitches in the code editors and sometimes the code for a device just stops working and the only solutions is to remove the device definition and create a new.

- Hit and miss what line an error is reported on...

- Some mistakes just hangs/give no visible errors - Ex. by mistake calling QuickApp:debug("FOO")

- api.get(“/refreshState?<lastID>”) seems to hang for ~30s if there is no new event available, instead of immediately returning an empty event/change table.

- net.HTTPClient() seems to have an issue with ‘POST’ command and json payloads in many cases as neither the success or the error handler seems to get called (no problem with GET commands) Turned out that I needed to add a 'Connection: keep-alive' header when doing a POST to get the result back - never needed that in the past...

 

However, I expect these to be fixed over time… and people start coding will report in more bugs...

 

Disclaimer 3: I may have missed stuff that are available and I haven’t been able to discover  :-) 

 

So, have anyone else discovered some new programming concepts on the HC3? or additional methods/tricks available in QuickApps ?

Meanwhile, I will update this thread with new stuff as I continue to make discoveries in this undocumented landscape... ;-) 

 

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

Recommended Posts

  • 0
  • Inquirer
  • So, working a bit more I think that the concept of "customEvents" were promising but they missed it's potential,

     

    customEvents have a 'name' and a 'userDescription' field.

    You have to define a customEvent before you use it.

     

    Ex. api.post("/customEvents/",{name='myCustomEvent', userDescription="42"})

    That part makes sense as you then, in a block scene, can get a list of available customEvents to use in your block scene.

     

    Then you "emit" a customEvent

    Ex. api.post("/customEvents/myCustomEvent")

    The event has to be defined first. That's ok.

    ...and you can emit them multiple times, like a lunch event: api.post("/customEvents/Lunch")

     

    However, in this shape and form it doesn't really give us much more than a global variable, except that it can generate a trigger even if it's value (userDescription) hasn't changed (because we can emit events, global variables only generate triggers when their values changes)

    However, it's not difficult to change the value on a global var to generate a trigger - and trigger a scene.

    Please login or register to see this code.

    So, new new ways to express old functionality is usually called "sugar" - and that's what the events turn out to be.

     

    What really would have made events provide something new would have been to allow them to carry a message when posted.

    Now you can only trigger on the customevent name

    Please login or register to see this code.

    Which is kind of lame (why need operator? can you trigger on "!=" ... no.)

    Assume instead that customevents carried a message

    Please login or register to see this code.

    Much more powerful - and kind of how event mechanisms work in most other platforms.

    Can we use the "userDescription" field for the message?

    Well, kind of.

    Conditions can't match on it but that could be fixed.

    However, the problem is that an customevent can only have one user description at the time. (remember they are just sugar coated global variables).

    This means that we can't post 2 events with different messages before they are picked up by the scenes that listen to them. Because the second post would overwrite the userDescription of the first post. (the same problem we have with using global variables to send arguments between scenes).

     

    So, nice attempt - but didn't really give us anything new... 

     

     

    Link to comment
    Share on other sites

    • 0
    On 2/9/2020 at 1:07 PM, petrkl12 said:

    @jgab Thanks, it works ? I have added also other settings (Accept, Connection etc.) to header based on Chrome

     

    can you please give an example? i can't get ik working

    Link to comment
    Share on other sites

    • 0

    I am trying to store a function which adds minutes to a stored time using the following code

    function QuickApp:addMinutesToTime(x,y)
        do
            local fmt = string.format
            function str2sec(str) local h,m,s=str:match("^(%d%d):(%d%d):?(%d*)"return 3600*h+60*m+(s~="" and s or 0end
            function sec2str(sec) return fmt("%02d:%02d%s",math.floor(sec/3600),math.floor((sec % 3600)/60),sec % 60 > 0 and fmt(":%02d",sec%60or ""end
            x=sec2str(str2sec(x)+(y*60))
        end
    end
    I would like to call it from a scene and i use it like this
    local add60 = fibaro.call(275"addMinutesToTime","09:00"60) 275 is the qa device
    it returns me nil, can someone please explain what i am doing wrong please?
    thank you
    Link to comment
    Share on other sites

    • 0
  • Inquirer
  • 13 minutes ago, Jay Ess said:

    I am trying to store a function which adds minutes to a stored time using the following code

    function QuickApp:addMinutesToTime(x,y)
        do
            local fmt = string.format
            function str2sec(str) local h,m,s=str:match("^(%d%d):(%d%d):?(%d*)"return 3600*h+60*m+(s~="" and s or 0end
            function sec2str(sec) return fmt("%02d:%02d%s",math.floor(sec/3600),math.floor((sec % 3600)/60),sec % 60 > 0 and fmt(":%02d",sec%60or ""end
            x=sec2str(str2sec(x)+(y*60))
        end
    end
    I would like to call it from a scene and i use it like this
    local add60 = fibaro.call(275"addMinutesToTime","09:00"60) 275 is the qa device
    it returns me nil, can someone please explain what i am doing wrong please?
    thank you

     

    I guess you should return x from the addMinutesToTime function....

    but that's not your real problem. The real problem is that fibaro.call doesn't return the value from the function called.

    Link to comment
    Share on other sites

    • 0
  • Inquirer
  • 1 hour ago, jgab said:

     

    I guess you should return x from the addMinutesToTime function....

    but that's not your real problem. The real problem is that fibaro.call doesn't return the value from the function called.

     

    So, you need to do something like this

    In Scene

    Please login or register to see this code.

    and the QA serving up the request needs to look something like

    Please login or register to see this code.

    It can be extended to relay error messages and multiple return values.

     

    Instead of a fibaro global we could use the sceneVariables available - but I haven't figured out how to set a sceneVariable from a QA (or from the rest API). Anyone?

    Link to comment
    Share on other sites

    • 0

    @jgab

    just thinking for the three lines of code would you store it in a function or just write it straight in to the scene? i was hoping for a quick way to store different functions and easily be able to use them in scenes but the way you are suggesting seems to be overkill unless the function is way more complex?

    Link to comment
    Share on other sites

    • 0
  • Inquirer
  • For this 3 lines of code I would include it in the scene. For typical scenes that triggers on something , does some action and then terminates it is usually not worth it to spend time to setup stuff. For scenes that run over a longer time it could be worthwhile... 

     

    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
    Answer this question...

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