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

Thanks again, this was indeed the problem.

 

FWIW here is the code for my MQTT listener QA. The QA stays alive if the broker disconnects or fails, during reboot for instance. Feel free to use or improve.

 

By the way, self:error("closed: "..json.encode(event)) in handleClosed throws an error in the emulator, but runs fine on the acutal HC3. So when you test, better comment out that line.

 

Please login or register to see this code.

 

Cheers,

 

jayrock

Link to comment
Share on other sites

  • Topic Author
  • 11 hours ago, jayrock said:

    Thanks again, this was indeed the problem.

     

    FWIW here is the code for my MQTT listener QA. The QA stays alive if the broker disconnects or fails, during reboot for instance. Feel free to use or improve.

     

    By the way, self:error("closed: "..json.encode(event)) in handleClosed throws an error in the emulator, but runs fine on the acutal HC3. So when you test, better comment out that line.

    Cheers,

    jayrock

    Do you have a stack trace/dump of the error that is thrown?

    Please login or register to see this code.

    Is it the json.encode that throws the error?

    • Like 1
    Link to comment
    Share on other sites

    Yes indeed,  json.encode(event) of the 'closed' event throws that error.  self:error("closed: ") works as expected. 

     

    I checked the type of event, which is table. Then I checked the contents of event with ipairs and it was an empty table. At that point I didn't bother digging further, as my code runs fine and it seemed to be a bug in the emulator when constructing the 'closed' event.

     

    I'll send a stack trace later when I'm at the development machine. IIRC the main message said that json.encode cannot take a function as an argument.

     

    /jayrock

    Link to comment
    Share on other sites

  • Topic Author
  • 55 minutes ago, jayrock said:

    Yes indeed,  json.encode(event) of the 'closed' event throws that error.  self:error("closed: ") works as expected. 

    I checked the type of event, which is table. Then I checked the contents of event with ipairs and it was an empty table. At that point I didn't bother digging further, as my code runs fine and it seemed to be a bug in the emulator when constructing the 'closed' event.

    I'll send a stack trace later when I'm at the development machine. IIRC the main message said that json.encode cannot take a function as an argument.

    /jayrock

    Most likely that the event contains a function or user data that the json.encoder can't encode (on the HC3 the event does not contain functions)

    I have pushed a new version of the emulator v. 0.147 that hopefully fix the problem.

    I don't use MQTT that much so I had missed this...

    Link to comment
    Share on other sites

    1 hour ago, jgab said:

    I have pushed a new version of the emulator v. 0.147 that hopefully fix the problem.

     

     

    Not quite.... to start with, here is the log with version 0.145:

     

    Please login or register to see this code.

     

    .... and here with 0.147:

     

    Please login or register to see this code.

     

     

    1 hour ago, jgab said:

    I don't use MQTT that much so I had missed this...

     

    Don't worry too much about this bug. It's a corner case.

     

     

    --------------------------------------------------------------------------------------------------

     

    Talking of bugs, here is another one that has been present in 0.145 when running "Download fibaroapiHC3.lua" from the file menu in ZeroBrane:

     

    Please login or register to see this code.

     

    Again I personally didn't bother to troubleshoot this and just downloaded the file manually.

     

    Hope it helps.

     

    /jayrock

    Edited by jayrock
    Link to comment
    Share on other sites

  • Topic Author
  • Sorry, thought is was only 'function' - pushed v 0.148 with yet another fix.

    Thanks.

    10 minutes ago, jayrock said:

    ---------------------------------------------------------------------------------------------

     

    Talking of bugs, here is another one that has been present in 0.145 when running "Download fibaroapiHC3.lua" from the file menu in ZeroBrane:

     

    Please login or register to see this code.

     

    Again I personally didn't bother to troubleshoot this and just downloaded the file manually.

     

    Hope it helps.

     

    /jayrock

    You need latest version of the fibaro plugin.

    Please login or register to see this link.

    Sorry, they don't come with version numbers.

    • Like 1
    Link to comment
    Share on other sites

     

    17 minutes ago, jgab said:

    Sorry, thought is was only 'function' - pushed v 0.148 with yet another fix.

     

    No worrries. We didn't know about 'thread' before your first fix. 0.148 solved it, thanks.

     

     

    --------------------------------------------------------

     

    12 minutes ago, jgab said:

    You need latest version of the fibaro plugin.

     

    Just did that. The menus look different, so there has been an update, that's good. But the issue remains:

     

    Please login or register to see this code.

     

    Link to comment
    Share on other sites

  • Topic Author
  • Because QuickApps can consist of multiple files these days it's quite easy to prepare "library" files with useful code that one resuse between QuickApps.
    THere is always a "main" file in a QA and then one can add additional files.
      
    If I create a QA file named "my funcs" and add the code
    "my funcs":

    Please login or register to see this code.


    the functions foo(x) and bar(x) will be available as lua global function and accessible in the "main" file too.
    "main":

    Please login or register to see this code.


    If we had declared the lua functions local, they would not be visible outside the file "my funcs".
    "my funcs":

    Please login or register to see this code.


    "main"

    Please login or register to see this code.

    So, our shared functions need to be global, but we don't want to pollute the global namespace of lua with a lot of shared function names that may collide with functions name  we use in "main".
    A  simple approach is put the  functions in lua table. Then we only have one global name.
    "my funcs":

    Please login or register to see this code.


    "main"

    Please login or register to see this code.

    This is nice, but we can do even better. In the above example, the shared file "my funcs" decides to put the functions in the global 'myFuns'. It would be nice if the user's "main" could decide where to put the functions.

    "my funcs":

    Please login or register to see this code.

    "main":

    Please login or register to see this code.

    This is nice. We only have one global table '_modules' and our "files", let us call them 'modules', register their function with the shared table.
    Our "main" that wants to use the functions can fetch the modules  it wants from the _modules table. We can easily have multiple modules this way. The only requirement is that they register with unique names.

     

    We can actually improve on this. We have one problem; "load order". When we start to have many modules we will arrive at the situation  that we have one module that needs another module. We would like to "load" the modules in the right order. The problem is that we can't control in what order the HC3 loads the files. Except that it loads "main" last.
    Another aspect is that a module may want to run some initialization code, but we only want to run it if we need or "load" the module.
    The solution is to let the modules register an initialization function instead of just a table  as in the previous example."my funcs1":

    Please login or register to see this code.

    "my funcs2":

    Please login or register to see this code.


    "main":

    Please login or register to see this code.

    No we can control the order modules are loaded so if some modules needs another we can load them in the correct order. We also call an 'load' function for each modules if the modules needs to do some setups.

    In the example myFuns2 uses functions from myFuns1. The trick we use is that load() saves the function table and returns it if called again.


    To the load() function we also pass the QuickApp self so the modules can use QuickApp functions.
    Each module register a table with the load(self) function but also other nice to have fields, like version and author. In this case we also register a url to a location where the file can be downloaded. This can be used to implement some "auto update" mechanism whereas if a newer version of the files exists it is automatically downloaded adn replace the cutrrent file.

     

    We also provide a QuickApp:module(<name>) function that help "main" to load modules.

     

    The

    Please login or register to see this link.

    I use myself works in a similar way as the example above...

    Edited by jgab
    Cleaner module structure
    • Like 4
    • Thanks 1
    Link to comment
    Share on other sites

  • Topic Author
  • On 12/22/2020 at 1:27 PM, jayrock said:

     

     

    No worrries. We didn't know about 'thread' before your first fix. 0.148 solved it, thanks.

     

     

    --------------------------------------------------------

     

     

    Just did that. The menus look different, so there has been an update, that's good. But the issue remains:

     

    Please login or register to see this code.

     

     

    This is a really strange error... I have uploaded v 0.149 that may fix the issue. Let me know. In any case it is very strange....

    Link to comment
    Share on other sites

    Questions regarding UI in QuickApp

     

    @jgabI have studied your examples of dynamic UI as I need a QuickApp where I can switch between two or more kinds of UI.

    However, I have some questions that I hope you / others can help with.

     

    In the attached QA I have two different UIs, which I can switch between by pressing the Default and Special buttons.

     

    To see the changes, you need to refresh the page, eg by switching to another QA and back again.

    Once I have selected Special, I have several different controls, such as

        Label, Button, Select (Dropdown-Simple), Select (Dropdown-multi), Slider.

    I'm missing a Switch, but I could not get it to work.

     

    Please login or register to see this attachment.

     

     

    Here are my questions:

     

    1)    When I have changed the page with the new controllers, how do I get the QuickApp to refresh the UI.


    2)    I fill in information in the UI in another QA using self:updateView("name", "type", "val") for the different types.

            I have also found some callbacks that work.

            Type                  Update                                                                                                 Callback

           Label              self:updateView("name", "text", "message")

           Image             self:updateView("name", "url", "http: //xx.yy/zz.png")

           Slider              self:updateView("name", "value", "xx")                            onChanged

       

           However, there is a number of things I can't make work.

           I need a little help with the following. Both regarding updateView and callback.

     

           select             self:updateView("name", "?????", "???")                          onToggled    (for single)

                                                                                                                             ???                 (for multi)

            switch           self:updateView("name", "?????", "???")                          ???

     

    3)     Like I said, I do not know how to build a Switch control.
            How to build the switch in the $ jason structure

     

     

    4)    A little extra question.
           How can I add extra commands to a QuickApp so I can select them in a Block scene.
           I know it has something to do with the "actions": ["turnOn":0, "turnOff":0, "setLevel":1]

     

    Thanks in advance

     

     

     

    Please login or register to see this attachment.

    Link to comment
    Share on other sites

  • Topic Author
  • 10 hours ago, [email protected] said:

    Questions regarding UI in QuickApp

     

    @jgabI have studied your examples of dynamic UI as I need a QuickApp where I can switch between two or more kinds of UI.

    However, I have some questions that I hope you / others can help with.

     

    In the attached QA I have two different UIs, which I can switch between by pressing the Default and Special buttons.

     

    To see the changes, you need to refresh the page, eg by switching to another QA and back again.

    Once I have selected Special, I have several different controls, such as

        Label, Button, Select (Dropdown-Simple), Select (Dropdown-multi), Slider.

    I'm missing a Switch, but I could not get it to work.

    I couldn't either. Seems to be buggy.

     

    10 hours ago, [email protected] said:

     

    Please login or register to see this attachment.

     

     

    Here are my questions:

     

    1)    When I have changed the page with the new controllers, how do I get the QuickApp to refresh the UI.

    You can't. at least what I know.

    I was thinking about faking a "save QA" command - however, the "custom UI" is reset when re-saving the QA in the web editor UI. The is actually a reason why these custom UIs are not that useful. If someone opens the QA editor and happens to re-save it the custom controls are lost.

    I'm kind of waiting myself for official support for new controls (and battery layout)

     

    10 hours ago, [email protected] said:


    2)    I fill in information in the UI in another QA using self:updateView("name", "type", "val") for the different types.

            I have also found some callbacks that work.

            Type                  Update                                                                                                 Callback

           Label              self:updateView("name", "text", "message")

           Image             self:updateView("name", "url", "http: //xx.yy/zz.png")

           Slider              self:updateView("name", "value", "xx")                            onChanged

       

           However, there is a number of things I can't make work.

           I need a little help with the following. Both regarding updateView and callback.

     

           select             self:updateView("name", "?????", "???")                          onToggled    (for single)

     

                                                                                                                             ???                 (for multi)

            switch           self:updateView("name", "?????", "???")                          ???

    Actually I never tried it...

    I thought that self:updateView("name", "values",{<value>})  would work but it doesn't... I would guess it's not implemented....

     

    10 hours ago, [email protected] said:

     

    3)     Like I said, I do not know how to build a Switch control.
            How to build the switch in the $ jason structure

    I believe it's buggy...

    10 hours ago, [email protected] said:

     

     

    4)    A little extra question.
           How can I add extra commands to a QuickApp so I can select them in a Block scene.
           I know it has something to do with the "actions": ["turnOn":0, "turnOff":0, "setLevel":1]

     

    You can only use the actions defined for the QA. The actions are pre-defined depending on the type of the QA. you can't add custom actions. So choose your types with care....

     

     

    10 hours ago, [email protected] said:

     

    Thanks in advance

     

    Anyway, good work - but I would be hesitant to use custom UIs at the moment...

     

    10 hours ago, [email protected] said:

     

     

     

    Please login or register to see this attachment.

     

    Link to comment
    Share on other sites

    On 12/26/2020 at 7:02 AM, jgab said:

    This is a really strange error... I have uploaded v 0.149 that may fix the issue. Let me know. In any case it is very strange....

    0.149 indeed solves the error. I used the plugin v0.3 from yesterday.

     

    Thanks!

    Link to comment
    Share on other sites

  • Topic Author
  • This post explains a bit more what's going on with child devices. It is recommended to read the previous post on Lua functions and classes as well as Fibaro's own tutorial on QuickAppChild devices.

    Please login or register to see this link.

    If you spot any faulty information or something that could be clarified better let me know and I will update and give you credit.

     

    Child devices are QAs that "belong" to and is created by a "mother" QA.

     

    "Mother QA"

       |

       +--> Child 1 QA   

       |

       +--> Child 2 QA   

       |

       +--> Child 3 QA

       

    Children are, and look like regular QuickApp devices on the HC3. They list among "other" devices and have device IDs. The difference is that they don't have their own code, instead the code for the children resides in the mother QA definition (file) - which typically is the QA that creates the children.

    They are always listed directly under the mother QA, so it breaks sorting on deviceIDs, but it is usually more practical to have them grouped together.

     

    Typically, child QAs represent external devices/services that we want to represent on the HC3. Usually, the external devices are accessed through HTTP or TCP. However, child QAs, like regular QAs, can serve any function. Maybe a QA that can create multiple alarm clock child QAs or something similar.

     

    Like regular QAs, child QAs are also assigned a type. Because it's not possible to add custom UIs to child QAs (at the moment), we need to select the type with care as it decides what UI controls the child will have.

    There are a lot of types to choose between. To print a list of types one can run this code:

    Please login or register to see this code.

     

    Not all devices have UIs, so it's a test and try.

     

    We can access the QA structure of a child like we do with a regular QA.

      api.get("/devices/77")

    will return the QA structure for a child device with id 77. What is special with child QAs is that they have a structure key named 'parentId', that is the id of the mother QA.

    We can retrieve the children of a mother QA with

    api.get("/devices?parentId=66")

    if the mother QA has the id 66. The query will return all devices that has a parentId key equal to 66.

    A child will also have an interfaces property of type quickAppChild

     

    Please login or register to see this code.

     

    So, we have said that child devices are like regular QuickApp devices. What happens when we press a UI button on a child device, or call fibaro.call(childID,"action",...) is that the action and UI events are sent to the parent of the child QA. The system sees the parentId of the device and sends the events to the parent.

    The parent QA will receive events, where some of the events have IDs that corresponds to its children. It then finds the code for the child with that ID and call corresponding methods defined for that child.

     

    How do we implement the code for a child in a QuickApp?

     

    Like the QuickApp class that helps us to define the main/mother QuickApp, quickApp children are usually implemented with the class QuickAppChild.

    A side note, QuickAppChild is not a subclass of QuickApp. QuickApp and QuickAppChild are subclasses of the class QuickAppBase. QuickAppBase implements everything a QuickApp needs, except for handling children. QuickAppChild is a simple subclass of QuickAppBase and doesn't seem to add any child specific methods

     

    class QuickAppBase (self:debug, self_trace,... self:updateView, self:updateProperty,...)

       |

       +--> QuickApp (self:createChildDevice, self:removeChildDevice, self:initChildDevices )

       |

       +--> QuickAppChild ()

     

    Please login or register to see this code.

     

    class Child1(QuickAppChild)

    says that our new Child1 class should be a subclass of QuickAppChild.

    This statement actually defines a method Child1() that we can use to create a new Child1 instance. We call it a 'constructor'.

    Please login or register to see this code.

     

    It's a bit more complicated. We usually want to initialize our new child in some way. That is accomplished with the function

    Please login or register to see this code.

    This defines an 'initializer' function, Child1:__init(device), that is called by the Child1's constructor. The first thing we do inside the initializer is to call the parent's initializer

    QuickAppChild.__init(self,device).

    The QuickAppChild's initializer expects 2 arguments. The 'self' of the object we are creating and the definition 'device' that is the table structure we get if we do api.get("/devices/77") - assuming that 77 was a QuickAppChild.

      

    The initializer for QuickAppChild will add fields to self, like self.id, self.name, self.type, self.properties.*.

    It copies those values from the 'device' structure.

     

    Where does 'self' come from in the initializer?

    Functions defined with ':' like

    function Child1:__init(device) 

    is a shortcut for 

    function Child1.__init(self,device) 

     

    Our construnctor Child1() on a high level is defined something like

    Please login or register to see this code.

     

    the '...' parameter makes sure that any argument, like 'device', that is passed as an argument to Child1 is passed on to the .__init function.

     

    Let's make a child device with a constructor that takes the nick name of a device.

    Please login or register to see this code.

     

    Now we can create a Child2 object with nickname 'Bob'

    Please login or register to see this code.

    As we will see later, additional arguments to child constructors are not so useful in practice, and there are other means to pass data to children when they are created.

     

    Anyway, where does 'device' come from?

    Creating a Child2 object doesn't actually create a child device. We should view the Child2 object as a "code" wrapper around the real child device that we create. Every time the QA starts/restarts it needs to re-create this code wrappers and associate them to the existing child devices. We will talk more about that later when we come to loading children at startup.

        

    The normal way to create a QuickAppChild object is to use the QuickApp function 

    function QuickApp:createChildDevice(options,constructor)

    Please login or register to see this code.

     

    This function does 2 things. It creates a child device on the HC3 and it creates a Lua object with the constructor that is associated with the device. It looks something like this:

     

    Please login or register to see this code.

     

    The /plugins/createChildDevice creates the QuickApp child device in the system. The QuickApp will be assigned the next available deviceId number. We can't choose IDs for devices we create. The api call returns the QuickApp child structure, where we can find the id that it got assigned.

    It then creates the Child instance, using the constructor we have specified, Child2. It sends the device structure as an argument and that is how our QuickAppChild objects get that 'device' argument.

     

    The QuickApp also has a table, self.childDevices, where it maps the childrens ID's to the object it creates. It's a convenient way to keep track of child devices for the QA.

     

    It also assigns the child's 'parent' field to be the QuickApp, the 'mother'. It means that inside a child method we can call a parent method easily.

     

    Please login or register to see this code.

     

    Note, looking at the createChildDevice definition above, we see that the child object is created before we assign it's parent field to the mother QA. This means that inside the child's initializer, .__init(), parent is not available. It's not until we exit .__init() it becomes set. So, don't use self.parent inside child's .__init method.

    In fact, QuickAppChild object has a :onInit() method like the main QuickApp has. It's called from the QuickAppChild's initializator. However, it also means that self.parent is not set.

     

    Fibaro gives us 2 ways to initialize the child object. In the .__init() method or the :onInit() method of the child object. What's better is a matter of taste. Because we need to define the .__init() anyway, to call the QuickAppChild.__init(self,device), I personally prefer to do the rest of the initialization there and ignore the child's :onInit() function.

     

    Ok, now we know how to create QuickAppChildren.

     

    This means that your QA needs to deal with two tasks. 

    1. create children that are needed

    2. when starting up, load existing children.

     

    The first task is usually done by some initialization code that knows what child devices to create. It can be that we want to create child devices for various sensors from an external device that we access via HTTP. It could also be that we dynamically create children when we press a UI button on the mother QA. In any case, your main QA knows what children to create.

     

    However, when the QA restarts it may already have created children, children that exists as devices registered in the HC3. It is then the QA's responsibility to load in these existing children, create the corresponding QuickAppChildren objects and make the association.

      

    ...and there is a function for that. 

    function QuickApp:initChildDevices(map)

    The map is a table that maps device types to class constructors for our children.

    Ex.

    {["com.fibaro.binarySwitch"] = Child1}

     

    That function is quite primitive and will only allow one type to have one constructor (but several types can have the same constructor)

    The way it works is something like to:

    Please login or register to see this code.

     

    This means that your QA at startup first needs to load devices and if not the number of devices expected, create the missing ones.

    One way to do that could be

    Please login or register to see this code.

     

    The createMissingChildren function needs to see what children are missing and create them.

    The reason we may need this logic is that users may delete child devices in the UI by clicking on their trashcan icon.

    The good thing is that the list self.childDevices is updated when that happens. However, the mother QA doesn't get any warning or callback.

    A way to trap that is to listen on /refreshStates and see the DeviceRemovedEvent for a child beloning to the QA. Other approaches are to patch QuickApp:removeChildDevice(id) or install an onAction handler. We will come back to that later.

     

    If we use the QuickApp function self:removeChildDevice(id) the child device is removed and self.childDevices is also updated. 

     

    The Fibaro guide to QuickAppChildren advices us using a table to map between created QA children and the external services/devices they represent. So the example is a table that maps the child HC3 deviceIds to the deviceIds of external Hue devices.

    The reason for having this table is that a typical structure of the QA is that the main QA runs a loop polling the external devices and then calls a method with the new data to the corresponding child QA

      

    "main QA":

    Please login or register to see this code.

     

    fetchData's second argument is the "success handler" that is called when data is read. Usually, HTTP requests are asynchronous so this model is needed.

            

    If each child QA do their own polling, we don't need this map.

      

    "child QA":

    Please login or register to see this code.

     

    What we need to do is to give the external_ID to the child so it knows where to fetch data from.

    Here we use a function 'setup', but another method is to put that data in a quickAppVariable of the child.

     

    We can solve a couple of the above problems by creating our own 'initChildDevices'...

    First the ceateChild...

     

    Please login or register to see this code.

     

    This is similar to to provided QuickApp:createChildDevice(options,constructor) but is a bit more convenient.

    Please login or register to see this code.

    We give the name and type of the child. Then we provide the name (string) of the class that should be used to create a child QA. We can also add quickAppVariables that the child should have.

    The advantage is that we store the external_ID talked about above in the child QA's quickAppVariable when we create the child. It also stores the name of the class as a quickAppVariable. We use that in our own initialize function.

     

    Our function uses the original QuickApp:createChildDevice function to create the child. We could have used the api function

    Please login or register to see this code.

    or the provided function

    Please login or register to see this code.

    to create the child QA.

    Note, that these primitives only create the child QA device, not the class object in our code.

     

    Please login or register to see this code.

     

    This function is similar to 'initChildDevices' but it leverages the fact that the class name is stored as a quickVar in the child so it knows what type of object it should construct for the child. This way we don't need the map that the original 'initChildDevices' needs, and we have more flexibility in how to map classes and types.

    A bonus is that it returns the number of child devices it loaded so we can compare that to how many child devices we expect to have...

     

    In our loadChildren() function we redefine the regular initChildDevices() function to do nothing. This is because the QuickApp will call initChildDevices() it if we haven't called on it explicitly in our code. Redefining it to do nothing stops it from trying to "initialise" the children again.

     

    So, this means that we have loaded all children and they are available in the table

    self.childDevices.

    If we know the id of a child we can get the child instance

    Please login or register to see this code.

    ..and ex. call a method like turnOn() in this case

    In createChild function presented earlier we could add quickVars to the children we created and in particular we could add the ID of the external device the child should mirror.

    Ex. if our children our mirrors of Philips Hue devices we store the hueID as a quickVar in the child.

    That means that when the child is initialised we can retrieve the quickVar and store it as a more accessible field in the child instance (quickVars are a bit inefficient to retrieve with self:getVariable)

    Please login or register to see this code.

     

    If we have the childID and we need the hueID we can just do

    Please login or register to see this code.

    One thing is missing now. We would like to have the reverse mapping. If we have the hueID we would like to get the childID. We could look through the self.childDevices table everytime

    Please login or register to see this code.

    It may be a bit inefficient, and instead we could have a reverse mapping table.

    hueIDmap = {}

    and everytime a child is initialised it will update the table with it's hueID and childID.

    The issue we have is that if a child QA is deleted we would like to remove the mapping from the table.

    The self.childDevices table is kept updated by the QuickApp but our own hueIDmap is not.

    If we delete the child QA in our Lua code it's easy to update our table also. If the user deletes the child QA in the UI we don't get notified. Earlier in the post it was noted that we could monitor /refreshStates, patch self:removeChildDevice(id) or install an onAction handler to catch the fact that a child was removed. Lets do the second option.

    Please login or register to see this code.

    Every time a child is deleted we update our reverse map.

    This means that we have self.childDevices and hueIDmap that can map between child ID and Hue ID and they are kept updated and in sync.

     

    I would not recommend child QAs to run their own poll loops like in the previous example. The reason is that it is difficult to control the frequency of polls, taken all the children QAs together. It's usually much better to let the main QA poll the external devices and give the data to children.

     

    We can still do that with the model above.

    Assume that the Child class we have, uses the external_ID given as a quickVar when it was constructed.

    Please login or register to see this code.

    In the child's initializer we fetch the ID quickVar and store it in an instance variable (self.external_ID). We do that for convenience.

      

    Our main loop in the QA can then look like

    Please login or register to see this code.

     

    ...or we could have a child method that does the job of :newData and fetchData

    Please login or register to see this code.

     

    I have a Philips Hue QA that creates child QAs for the Hue devices.

    If there are many children I decide to call the Hue hub to get all the devices data in each single poll.

    I then loop over all my children giving them all the data, and the children select the data that is relevant for them.

    This works well as each child QA knows what Hue ID it is associated with.

      

    We could actually use child QAs without using the QuickAppChild class.

    In the previous examples we have implemented createChildDevice and initializeChildDevices, so we could replace them with non-object oriented code.

    The only problem is that we need to make sure that fibaro.call(childID,<method>,<args>) works, and we have to fix so that UI interactions, like button clicks are sent to the right piece of code handling the child logic.

     

    ...I will add to this post an example how to accomplish it. It's mostly an academic exercise but it gives some insights how events are handled for child QAs

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

    Jan, in your arming QA I like to minimize the number of msgs.

    I use 7 partitions and I like to recieve one message  if a combination of the partitions are armed I hope it will be possible?

    How to exclude in the first line partition 4?

     

      rule("{1,3}:alarm='watch'") -- Watch partitions present & guestroom upstairs if they are armed
        rule("{1,3,4}:alarm='watch'") -- Watch partitions present, guest room up- and downstairs if they are armed.

    Thanks in advance,

    //Sjakie

     

    Link to comment
    Share on other sites

    On 12/30/2020 at 3:42 PM, Sjakie said:

    Jan, in your arming QA I like to minimize the number of msgs.

    I use 7 partitions and I like to recieve one message  if a combination of the partitions are armed I hope it will be possible?

    How to exclude in the first line partition 4?

     

      rule("{1,3}:alarm='watch'") -- Watch partitions present & guestroom upstairs if they are armed
        rule("{1,3,4}:alarm='watch'") -- Watch partitions present, guest room up- and downstairs if they are armed.

    Thanks in advance,

    //Sjakie

     

    Just following - i'm also having ALARM on the priority one list

    is there a QA ? reading "arming QA" :-)

    also 

     rule([[#alarm{id='$id',property='armed',value=true} =>
            user:msg = log("Alarm partition '%s' is now armed",id:alarm.name)
            ]])

    in LUA #alarm is the length of a table, but what is the meaning here?

     

    Edited by ChristianSogaard
    p
    Link to comment
    Share on other sites

  • Topic Author
  • 35 minutes ago, ChristianSogaard said:

    Just following - i'm also having ALARM on the priority one list

    is there a QA ? reading "arming QA" :-)

    also 

     rule([[#alarm{id='$id',property='armed',value=true} =>
            user:msg = log("Alarm partition '%s' is now armed",id:alarm.name)
            ]])

    in LUA #alarm is the length of a table, but what is the meaning here?

     

    # inside rules are used to define events.

    #foo is the same as {type='foo'}

    #foo{a=9} is he same as {type='foo', a=9}

     

    #alarm{id='$id',property='armed',value=true}

    is the same as 

    {type='alarm', id='$id', property='armed', value=true}

     

    #alarm{property='armed' ...} is posted by the HC3 when a section is armed, and our rule triggers on that and carries out some actions.

     

    To get the size of a table inside a rule use the function size(<table>)

    To arm sections on the HC3 use the app, the web GUI or use lua to arm a section.

    ER4 can react on sections about to be armed (giving time to cancel it if ex. windows are open), being armed, and breached.

    • Like 1
    Link to comment
    Share on other sites

    Jan,

    If I proper understand, will this do it?

    rule("{1,3}:alarm='watch' & {4}:property='armed',value=false") -- Watch partitions 1 and 3 if they are armed

    rule("{1,3,4}:alarm='watch'") -- Watch partitions 1, 3 and 4 if they are armed.

     

    I cant test now because I dont have the devices for arming in HC3, is planned as all goes well next week but I like to finish off what is still on the to do list.

    btw if a QA is having a few errors thta will stop the complete QA in executing or it skipp only the error part?

    Awaiting your valuable advice,

    //Sjakie

    Link to comment
    Share on other sites

    Jan,

    Sometimes it is simple, if you see it!

    rule("{1,3}:alarm='watch' & hal.kamerbeneden:isOn"-- Watch partitions present & guestroom upstairs if they are armed
     
    For this one I need your expertise in HC2 I use Jompa's OWM to retrieve weather info to use as:
        rule([[@sunrise-00:25:00 & woonKamer.gordijnVoor:isClosed =>
            || (label(454,'lblWeather')=='overcast clouds') >> log('overcast clouds--85-100%'); post(#morningCurtain,+/00:07); log('gordijnen openen');    
            || (label(454,'lblWeather')=='broken clouds') >> log('broken clouds--51-84%'); post(#morningCurtain,+/00:10); log('gordijnen openen');

    I can get my weather info with:

    Please login or register to see this link.

    I tryed a few things to get it working but here I miss the knowledge to finalize.
    but how to join together?
    Thanks in advance,
    //Sjakie
     
    Link to comment
    Share on other sites

  • Topic Author
  • 4 hours ago, Sjakie said:

    Jan,

    Sometimes it is simple, if you see it!

    rule("{1,3}:alarm='watch' & hal.kamerbeneden:isOn"-- Watch partitions present & guestroom upstairs if they are armed
     
    For this one I need your expertise in HC2 I use Jompa's OWM to retrieve weather info to use as:
        rule([[@sunrise-00:25:00 & woonKamer.gordijnVoor:isClosed =>
            || (label(454,'lblWeather')=='overcast clouds') >> log('overcast clouds--85-100%'); post(#morningCurtain,+/00:07); log('gordijnen openen');    
            || (label(454,'lblWeather')=='broken clouds') >> log('broken clouds--51-84%'); post(#morningCurtain,+/00:10); log('gordijnen openen');

    I can get my weather info with:

    Please login or register to see this link.

    I tryed a few things to get it working but here I miss the knowledge to finalize.
    but how to join together?
    Thanks in advance,
    //Sjakie
     

    You have to look if someone have done a HC3 QA for openweather. It's nothing that I'm using myself.

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