diff --git a/doc/devices_classes_branch.txt b/doc/devices_classes_branch.txt index ddcf1cd..c128589 100644 --- a/doc/devices_classes_branch.txt +++ b/doc/devices_classes_branch.txt @@ -402,33 +402,27 @@ Router written and integrated in energenie.loop() -No Discovery yet in Registry +Design for discovery done -------------------------------------------------------------------------------- TODO NEXT -* think a bit about discovery, we might actually be able to get this - working as a separate service first time round +* check design for discovery with user requirements -* implement auto device registration in the router (poor mans discovery) - when a message comes from an unknown sensorid, - build a device class instance wrapper for it - put it in the registry - knit it to the router - then call it's receive handler +* implement discovery code -* make the above auto device registration optional with a config flag in router +* write test cases in Registry_test.py to test the 4 different discovery types -* Write discovery properly as a service in Registry - When an unknown device is found, add it to the registry - if the discovery process is enabled, and route it's address. +* put a discovery agent configuration in monitor_mihome.py and re test + ---- * Need the registry to be persistent with save and load - + choose a file format that is human readable, like a config file? + mostly the registry is read, occasionally it is written ---- @@ -441,23 +435,14 @@ ---- -* when a join request comes in, should optionally schedule a join_ack - going back, so that the device turns it's light off. This could also - be a way to manually pair up devices with an app, as it means - the user has pressed the join button, so they have physical - sight of the device. - ----- - Test with real radio * add back in the loop() call in the monitor_mihome.py program +----- ----- -At this point, probably ready to write a set of test cases, -run the tests, and merge back to master to release this new -functionality to the wild. +* update the test instructions and re-test everything before merge +* merge to master after test END diff --git a/src/energenie/Registry.py b/src/energenie/Registry.py index 8e8b6de..77738ef 100644 --- a/src/energenie/Registry.py +++ b/src/energenie/Registry.py @@ -241,16 +241,16 @@ # address might be a string, a number, a tuple, but probably always the same for any one router self.routes[address] = instance - def incoming_message(self, address, payload): + def incoming_message(self, address, message): if self.incoming_cb != None: - self.incoming_cb(address, payload) + self.incoming_cb(address, message) if address in self.routes: ci = self.routes[address] - ci.incoming_message(payload) + ci.incoming_message(message) else: # unknown address - self.handle_unknown(address, payload) + self.handle_unknown(address, message) def when_incoming(self, callback): self.incoming_cb = callback @@ -260,14 +260,94 @@ #NOTE: this is the main hook point for auto discovery and registration self.unknown_cb = callback - def handle_unknown(self, address, payload): + def handle_unknown(self, address, message): if self.unknown_cb != None: - self.unknown_cb(address, payload) + self.unknown_cb(address, message) else: - # Default action is just a debug message, and drop the payload + # Default action is just a debug message, and drop the message print("Unknown address: %s" % str(address)) +class Discovery(): + """A Discovery agent that just reports any unknown devices""" + def __init__(self, registry, router): + self.registry = registry + self.router = router + router.when_unknown(self.unknown_device) + + def unknown_device(self, address, message): + print("message from unknown device:%s" % str(address)) + # default action is to drop message + # override this method in sub classes if you want special processing + + def reject_device(self, address, message): + print("message rejected from:%s" % (str(address))) + # default action is to drop message + # override this method if you want special processing + + def accept_device(self, address, message): + pass + # create device class instance from id information + # add to registry + # add to router + # forward message to new class instance for processing + #TODO: return the new device class instance to caller + + +class AutoDiscovery(Discovery): + """A discovery agent that auto adds unknown devices""" + def __init__(self, registry, router): + Discovery.__init__(self, registry, router) + + def unknown_device(self, address, message): + self.accept_device(address, message) + + +class ConfirmedDiscovery(Discovery): + """A discovery agent that asks the app before accepting/rejecting""" + def __init__(self, registry, router, ask): + Discovery.__init__(self, registry, router) + self.ask_fn = ask + + def unknown_device(self, address, message): + y = self.ask_fn(address, message) + if y: + self.accept_device(address, message) + else: + self.reject_device(address, message) + + +class JoinAutoDiscovery(Discovery): + """A discovery agent that looks for join requests, and auto adds""" + def __init__(self, registry, router): + Discovery.__init__(self, registry, router) + + def unknown_device(self, address, message): + # if it is not a join req + # route to unhandled message handler + # if it is a join req + # accept the device + # send join ack back to device (using new device class instance?) + pass + + +class JoinConfirmedDiscovery(Discovery): + """A discovery agent that looks for join requests, and auto adds""" + def __init__(self, registry, router, ask): + Discovery.__init__(self, registry, router) + self.ask_fn = ask + + def unknown_device(self, address, message): + # if it is not a join req + # route to unhandled message handler + # if it is a join req + # ask app + # if no + # reject device + # if yes + # accept device + # send join ack back to device (using new device class instance) + pass # Might rename these, especially when we add in other protocols diff --git a/src/monitor_mihome.py b/src/monitor_mihome.py index 84f2eeb..9286d72 100644 --- a/src/monitor_mihome.py +++ b/src/monitor_mihome.py @@ -46,10 +46,11 @@ purple.when_updated(new_data) try: + #TESTING: build a synthetic message + msg = energenie.OpenThings.Message(energenie.Devices.MIHO005_REPORT) + msg[energenie.OpenThings.PARAM_VOLTAGE]["value"] = 240 + while True: - #TESTING: build a synthetic message - msg = energenie.OpenThings.Message(energenie.Devices.MIHO005_REPORT) - msg[energenie.OpenThings.PARAM_VOLTAGE]["value"] = 240 #TESTING: Poke synthetic unknown into the router and let it route to unknown handler msg.set(header_sensorid=DUMMY_SENSOR_ID)