UniversalDevicesInc / pg3-python-interface

PG3 Python Interface
MIT License
0 stars 1 forks source link

controller's _nodes list brought from the shadows #5

Open Goose66 opened 4 years ago

Goose66 commented 4 years ago

Since loading previously discovered nodes from the database on nodeserver start() is a thing in at least two developer's multiple nodeservers, the controllers _nodes list (which is prepopulated by Polyglot with the node data from the database on start) should be made usable/visible to the nodeserver code, with a more intuitive interface. Not sure if there is a way to pre-populate controller.nodes from controller._nodes since the nodes may be of different classes, but a fairly robust method of repopulating controller.nodes from controller._nodes needs to be established.

Goose66 commented 4 years ago

Here's is, e.g., what I use today:

        # load nodes previously saved to the polyglot database
        # Note: has to be done in two passes to ensure system (primary/parent) nodes exist
        # before device nodes
        # first pass for system nodes
        for addr in self._nodes:           
            node = self._nodes[addr]
            if node["node_def_id"] == "SYSTEM":

                LOGGER.info("Adding previously saved node - addr: %s, name: %s, type: %s", addr, node["name"], node["node_def_id"])
                self.addNode(System(self, node["primary"], addr, node["name"]))

        # second pass for device nodes
        for addr in self._nodes:         
            node = self._nodes[addr]    
            if node["node_def_id"] not in ("CONTROLLER", "SYSTEM"):

                LOGGER.info("Adding previously saved node - addr: %s, name: %s, type: %s", addr, node["name"], node["node_def_id"])

                # add device and temperature controller nodes
                if node["node_def_id"] == "DEVICE":
                    self.addNode(Device(self, node["primary"], addr, node["name"]))
                elif node["node_def_id"] == "DIMMING_LIGHT":
                    self.addNode(DimmingLight(self, node["primary"], addr, node["name"]))
                elif node["node_def_id"] in DEVICE_COLOR_LIGHT_TYPES.values():
                    self.addNode(ColorLight(self, node["primary"], addr, node["name"]))
                elif node["node_def_id"] in ("TEMP_CONTROL", "TEMP_CONTROL_C"):
                    self.addNode(TempControl(self, node["primary"], addr, node["name"]))
bpaauwe commented 4 years ago

I agree.

would it be possible to do something cleaner with enumerate()? I'm thinking that we add an API function to the controller that returns enumerate on the _nodes list. So something like this in the interface:

def enumerateNodes():
    return enumerate(self._nodes, 0)

Then the node server code can look like:

for n in enumerateNodes():
    if node['node_def_id'] == 'whatever':
        self.addNode()
    if node['node_def_id'] == 'my_node_id':
        self.addNode()

As part of the API definition for enumerateNodes() we could define what the returned object looks like. That way it could be independent of the self._nodes data from the database. The database differences between cloud and local could then be handled in enumerateNodes() and not the node server code.

jimboca commented 4 years ago

Yes, agree and that was something I planned to work on, but with V3 coming I put it off.

Goose66 commented 4 years ago

Whether we have an enumerateNodes() function, or we just override __members__ of the controller.nodes property, we need to make sure it returns the nodes with primaries sorted first, so that they can be created before child nodes, and the two passes illustrated above won't be necessary.